标签:gid rip second 最小 org 整数 ase log 正整数
给定一个长度为\(n\) 的串,找出所有的\(border\) \((n\le 400000)\)
直接用哈希模拟判断即可。
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const int N=4e5+5,base=101;
char ch[N];int n;ull hsh[N],pw[N];
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
int main()
{
while(scanf("%s",ch+1)!=EOF)
{
n=strlen(ch+1);pw[0]=1;
for(int i=1;i<=n;++i)
hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
for(int i=1;i<=n;++i)
if(ghsh(1,i)==ghsh(n-i+1,n))printf("%d ",i);
puts("");
}
return 0;
}
给定长度为\(n\) 字符串,找出其长度最小的循环节。输出循环次数。\((n\le 10^6)\)
首先套路地将循环转化为\(border\) ,这样只用依次枚举循环节长度用哈希判断即可。
稍微注意下答案为\(1\) 的情况即可。
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const int N=1e6+5,base=71;
char ch[N];int n;ull hsh[N],pw[N];
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
int main()
{
while(scanf("%s",ch+1)!=EOF)
{
n=strlen(ch+1);
if(n==1&&ch[n]==‘.‘)break;
pw[0]=1;
for(int i=1;i<=n;++i)
hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
bool flag=0;
for(int i=n-1;i;--i)
if(n%(n-i)==0&&ghsh(1,i)==ghsh(n-i+1,n))
{
printf("%d\n",n/(n-i));
flag=1;break;
}
if(!flag)puts("1");
}
return 0;
}
给定长度为\(n\) 字符串和正整数\(m\) ,要求找出其尽量长的子串满足子串出现次数不小于\(m\) \((n\le 40000)\)
注意到这样一个性质:如果长度为某个\(X+1\) 的子串可行,那么长度为\(X\) 的子串也一定可行。
这样的话就满足单调性。于是可以二分答案\(X\) ,每次将长度为\(X\) 的子串的哈希值都存下来,再排序就可以统计出每个串出现了多少次,于是乎就可以判断是否可行了。
\(\mathcal O(n\log_2^2n)\)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int N=40005,base=71;
int n,k,ans;char ch[N];ull hsh[N],pw[N];
struct node{int id;ull val;}tmp[N];
inline bool operator<(const node&x,const node&y){return x.val!=y.val?x.val<y.val:x.id<y.id;}
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
inline bool ck(int x)
{
int cnt=0;ans=0;
for(int i=1;i+x-1<=n;++i)
tmp[++cnt]={i,ghsh(i,i+x-1)};
sort(tmp+1,tmp+cnt+1);
for(int i=1;i<=cnt;)
{
int j=i;
while(j<=cnt&&tmp[j].val==tmp[i].val)++j;
if(j-i>=k)ans=max(ans,tmp[j-1].id);
i=j;
}
return ans!=0;
}
int main()
{
while(scanf("%d",&k)==1&&k)
{
scanf("%s",ch+1);
n=strlen(ch+1);pw[0]=1;
for(int i=1;i<=n;++i)
hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
int l=1,r=n-k+2;
while(l+1<r)
{
int mid=(l+r)>>1;
ck(mid)?l=mid:r=mid;
}
ck(l)?printf("%d %d\n",l,ans-1):puts("none");
}
return 0;
}
给出两棵大小为\(n\) 的同构树,要求输出对应的节点。\((n\le 100000)\)
树哈希解决树同构问题。
树哈希是定义在有根树上的,对于树上节点\(u\) ,其哈希值\(h_u\) 为
当然这只是树哈希的一种较为通用且较不易出错的方式。
如果两棵树的根\(x,y\) 满足\(h_x=h_y\) 那么我们就认为这两棵树是相同的。
考虑无根树的情况。注意到一棵树至多有两个重心,因此可以把两个重心分别作为根做一遍哈希即可。
输出对应节点的话就采用递归,对儿子哈希值排序,然后递归,这样可以保证儿子的子树仍然是同构的。
下面给出一份不知何原因\(RE\) 的代码
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
using namespace std;
const int N=2e5+5;
typedef unsigned long long ull;ull h[N];
int n,now,rt1,rt2,sz[N],rt;string s[N];
map<string,int>id[2];
inline int gid(const string&ss,bool tp)
{
if(!id[tp].count(ss))id[tp][ss]=++now,s[now]=ss;
return id[tp][ss];
}
int tot,fi[N],ne[N<<1],to[N<<1];
inline void add(int x,int y)
{
ne[++tot]=fi[x],fi[x]=tot,to[tot]=y;
}
void fdrt(int u,int f,int&nmx)
{
sz[u]=1;int mx=0;
for(int i=fi[u];i;i=ne[i])
{
int v=to[i];
if(v==f)continue;
fdrt(v,u,nmx);sz[u]+=sz[v];
mx=max(mx,sz[v]);
}
mx=max(mx,n-sz[u]);
if(nmx>mx)nmx=mx,rt1=u,rt2=0;
else if(nmx==mx)rt2=u;
}
void ghsh(int u,int f)
{
h[u]=1;sz[u]=1;
for(int i=fi[u];i;i=ne[i])
{
int v=to[i];
if(v==f)continue;
ghsh(v,u);sz[u]+=sz[v];
h[u]+=h[v]*sz[v];
}
}
void solve(int u1,int f1,int u2,int f2)
{
cout<<s[u2]<<" "<<s[u1]<<"\n";
vector<pair<int,int> >tmp1,tmp2;
tmp1.clear(),tmp2.clear();
for(int i=fi[u1];i;i=ne[i])
{
int v=to[i];if(v==f1)continue;
tmp1.push_back(make_pair(h[v],v));
}
for(int i=fi[u2];i;i=ne[i])
{
int v=to[i];if(v==f2)continue;
tmp2.push_back(make_pair(h[v],v));
}
sort(tmp1.begin(),tmp1.end());
sort(tmp2.begin(),tmp2.end());
for(int i=0;i<tmp1.size();++i)
solve(tmp1[i].second,u1,tmp2[i].second,u2);
}
inline void clear()
{
now=0;id[0].clear();id[1].clear();
tot=0;fill(fi+1,fi+n+n+1,0);
}
int main()
{
std::ios::sync_with_stdio(false);
while(cin>>n)
{
string tmp;
for(int i=1,u,v;i<n;++i)
{
cin>>tmp;u=gid(tmp,0);
cin>>tmp;v=gid(tmp,0);
add(u,v),add(v,u);
}
int mx=n+1;rt1=rt2=0;fdrt(1,0,mx);
ghsh(rt=rt1,0);
for(int i=1,u,v;i<n;++i)
{
cin>>tmp;u=gid(tmp,1);
cin>>tmp;v=gid(tmp,1);
add(u,v),add(v,u);
}
mx=n+1;rt1=rt2=0;fdrt(n+1,0,mx);
ghsh(rt1,0);
if(h[rt1]==h[rt])solve(rt1,0,rt,0);
else ghsh(rt2,0),solve(rt2,0,rt,0);
clear();
}
return 0;
}
给出\(n\) 个大写字母串,要求选择尽量多的串使得每个大写字母在其中都出现偶数次。\((n\le 24)\)
出现偶数次让人联想到异或运算。
考虑对于每个串用一个二进制数表示它。二进制的某一位表示当前大写字母出现次数的奇偶性。
问题转化为选择尽量多的数满足其异或和为\(0\) 。
直接枚举复杂度炸裂,考虑meet in the middle
对于前\(\frac n2\) 个串所能形成的所有异或值,存在\(map\) 中。
然后再枚举后\(\frac n2\) 个串所能形成的所有异或值,每次都在\(map\) 中查询即可。
\(\mathcal O(2^{\frac n2}\log_22^{\frac n2})=\mathcal O(n\sqrt 2^n)\)
#include<bits/stdc++.h>
using namespace std;
int N,A[30];
char CH[30];
map<int,int>F;
inline int cont(int x)
{
int anss=0;
for(int i=0;(1<<i)<=x;++i)
if(x&(1<<i))++anss;
return anss;
}
int main()
{
while(scanf("%d",&N)==1)
{
for(int i=0;i<N;++i)
{
scanf("%s",CH);
A[i]=0;
for(char k=‘A‘;k<=‘Z‘;++k)
if(strchr(CH,k))A[i]+=(1<<(k-‘A‘));
}
int m=N/2,st=1<<m;F.clear();
for(int i=0;i<st;++i)
{
int g=0;
for(int j=0;(1<<j)<=i;++j)
if(i&(1<<j))g^=A[j];
F[g]=(!F.count(g)||cont(i)>cont(F[g]))?i:F[g];
}
int n=N-m,stt=1<<n,ans=0,anss=0;
for(int i=0;i<stt;++i)
{
int g=0;
for(int j=0;(1<<j)<=i;++j)
if(i&(1<<j))g^=A[j+m];
if(!F.count(g))continue;
int ct1=cont(F[g]),ct2=cont(i);
if(ct1+ct2>ans)
{
ans=ct1+ct2;
anss=F[g]|(i<<m);
}
}
printf("%d\n",ans);
if(ans)
for(int i=0;(1<<i)<=anss;++i)
if(anss&(1<<i))
printf("%d%s",i+1,(1<<(i+1))>anss?"":" ");
puts("");
}
return 0;
}
标签:gid rip second 最小 org 整数 ase log 正整数
原文地址:https://www.cnblogs.com/zmyzmy/p/14642700.html