【算法总结】字符串相关
【KMP算法】
〖模板代码〗
1 n=strlen(a);m=strlen(b); 2 for(int i=1;i<m;i++) 3 { 4 int j=f[i]; 5 while(j&&b[i]!=b[j])j=f[j]; 6 f[i+1]=b[i]==b[j]?j+1:0; 7 } 8 int j=0; 9 for(int i=0;i<n;i++) 10 { 11 while(j&&b[j]!=a[i])j=f[j]; 12 if(b[j]==a[i])j++; 13 if(j==m)printf("%d\n",i-m+1); 14 }
〖相关题目〗
1.【bzoj1355】[Baltic2009]Radio Transmission
题意:有一个字符串由某个字符串不断自我连接形成的字符串,这个字符串是不确定的,现在想知道它的最短长度是多少。
分析:hzwerの博客
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e6+5; 7 int n,j,fail[N]; 8 char s[N]; 9 int read() 10 { 11 int x=0,f=1;char c=getchar(); 12 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 13 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 14 return x*f; 15 } 16 int main() 17 { 18 n=read(); 19 scanf("%s",s+1); 20 for(int i=2;i<=n;i++) 21 { 22 while(j&&s[j+1]!=s[i])j=fail[j]; 23 if(s[j+1]==s[i])j++; 24 fail[i]=j; 25 } 26 printf("%d",n-fail[n]); 27 return 0; 28 }
2.【Codeforces Round #269 (Div. 2)】D. MUH and Cube Walls
题意:给你一个长度为n(1<=n<=2e5)的一排积木,长度分别为a[](1<=a[]<=1e9)。 有一个长度为m(1<=m<=2e5)的一排积木,长度分别为b[](1<=b[]<=1e9)。问你,第一排积木有多少个位点i,使得[i+0,i+m-1]这一段积木,之间增减幅度与b[]的整体增减幅度相同。 增减幅度肯定产生于相邻的积木之间。
分析:hzwerの博客
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=2e5+5; 7 int n,m,x,y,ans,a[N],b[N],fail[N]; 8 int read() 9 { 10 int x=0,f=1;char c=getchar(); 11 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 12 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 13 return x*f; 14 } 15 int main() 16 { 17 n=read()-1;m=read()-1; 18 x=read(); 19 for(int i=1;i<=n;i++)y=read(),a[i]=y-x,x=y; 20 x=read(); 21 for(int i=1;i<=m;i++)y=read(),b[i]=y-x,x=y; 22 if(m==0){printf("%d",n+1);return 0;} 23 if(n<m){printf("0");return 0;} 24 int j=0; 25 for(int i=2;i<=m;i++) 26 { 27 while(j&&b[j+1]!=b[i])j=fail[j]; 28 if(b[j+1]==b[i])j++; 29 fail[i]=j; 30 } 31 j=0; 32 for(int i=1;i<=n;i++) 33 { 34 while(j&&b[j+1]!=a[i])j=fail[j]; 35 if(b[j+1]==a[i])j++; 36 if(j==m)ans++,j=fail[j]; 37 } 38 printf("%d",ans); 39 return 0; 40 }
3.【bzoj3670】[Noi2014]动物园
题意:求出一个num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。输出Π(num[i]+1)。
分析:num指的是数量而不是长度。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e6+5; 7 const int mod=1e9+7; 8 int T,n,ans,j,fail[N],num[N]; 9 char s[N]; 10 int read() 11 { 12 int x=0,f=1;char c=getchar(); 13 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 14 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 15 return x*f; 16 } 17 int main() 18 { 19 T=read(); 20 while(T--) 21 { 22 scanf("%s",s+1); 23 n=strlen(s+1);ans=1; 24 num[1]=1;j=0; 25 for(int i=2;i<=n;i++) 26 { 27 while(j&&s[j+1]!=s[i])j=fail[j]; 28 if(s[j+1]==s[i])j++; 29 fail[i]=j;num[i]=num[j]+1; 30 } 31 j=0; 32 for(int i=1;i<=n;i++) 33 { 34 while(j&&s[j+1]!=s[i])j=fail[j]; 35 if(s[j+1]==s[i])j++; 36 while(j*2>i)j=fail[j]; 37 ans=1ll*ans*(num[j]+1)%mod; 38 } 39 printf("%d\n",ans); 40 } 41 return 0; 42 }
4.【bzoj1009】[HNOI2008]GT考试
题意:阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am。阿申想知道不出现不吉利数字的号码有多少种。
分析:hzwerの博客
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=25; 7 int n,m,mod,j,ans,num[N],fail[N]; 8 char ch[N]; 9 struct node{int a[N][N];node(){memset(a,0,sizeof(a));}}a,b; 10 int read() 11 { 12 int x=0,f=1;char c=getchar(); 13 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 14 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 15 return x*f; 16 } 17 node operator * (node a,node b) 18 { 19 node c; 20 for(int i=0;i<m;i++) 21 for(int j=0;j<m;j++) 22 for(int k=0;k<m;k++) 23 c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod; 24 return c; 25 } 26 int main() 27 { 28 n=read();m=read();mod=read();scanf("%s",ch+1); 29 for(int i=1;i<=m;i++)num[i]=ch[i]-'0'; 30 for(int i=0;i<m;i++)a.a[i][i]=1; 31 for(int i=2;i<=m;i++) 32 { 33 while(j&&num[j+1]!=num[i])j=fail[j]; 34 if(num[j+1]==num[i])j++; 35 fail[i]=j; 36 } 37 for(int i=0;i<m;i++) 38 for(int j=0;j<=9;j++) 39 { 40 int t=i; 41 while(t&&num[t+1]!=j)t=fail[t]; 42 if(num[t+1]==j)t++; 43 if(t!=m)b.a[i][t]=(b.a[i][t]+1)%mod; 44 } 45 while(n) 46 { 47 if(n&1)a=a*b; 48 b=b*b;n>>=1; 49 } 50 for(int i=0;i<m;i++)ans=(ans+a.a[0][i])%mod; 51 printf("%d",ans); 52 return 0; 53 }
【后缀数组】
〖模板代码〗
1 void build() 2 { 3 int *x=t1,*y=t2; 4 for(int i=0;i<n;i++)c[x[i]]++; 5 for(int i=1;i<m;i++)c[i]+=c[i-1]; 6 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 7 for(int k=1;k<=n;k<<=1) 8 { 9 int p=0; 10 for(int i=n-k;i<n;i++)y[p++]=i; 11 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 12 memset(c,0,sizeof(c)); 13 for(int i=0;i<n;i++)c[x[y[i]]]++; 14 for(int i=1;i<m;i++)c[i]+=c[i-1]; 15 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 16 swap(x,y); 17 p=1;x[sa[0]]=0; 18 for(int i=1;i<n;i++) 19 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 20 if(p>=n)break; 21 m=p; 22 } 23 }
【AC自动机】
〖相关资料〗
〖模板代码〗
1 struct Trie 2 { 3 int root,cnt; 4 int son[N*55][26],fail[N*55],num[N*55]; 5 bool mark[N*55]; 6 void init() 7 { 8 cnt=1;root=0; 9 memset(son,0,sizeof(son)); 10 memset(fail,0,sizeof(fail)); 11 memset(num,0,sizeof(num)); 12 memset(mark,0,sizeof(mark)); 13 } 14 int idx(char c){return c-'a';} 15 void ins(char s[]) 16 { 17 int len=strlen(s),cur=root; 18 for(int i=0;i<len;i++) 19 { 20 int id=idx(s[i]); 21 if(!son[cur][id])son[cur][id]=cnt++; 22 cur=son[cur][id]; 23 } 24 num[cur]++; 25 } 26 void build() 27 { 28 int head=0,tail=0,q[N*55],now,nex; 29 for(int i=0;i<26;i++) 30 { 31 now=son[root][i]; 32 if(now)q[tail++]=now; 33 } 34 while(head!=tail) 35 { 36 now=q[head++]; 37 for(int i=0;i<26;i++) 38 { 39 nex=son[now][i]; 40 if(!nex){son[now][i]=son[fail[now]][i];continue;} 41 q[tail++]=nex; 42 fail[nex]=son[fail[now]][i]; 43 } 44 } 45 } 46 void query(char s[]) 47 { 48 int len=strlen(s),j=root,c; 49 for(int i=0;i<len;i++) 50 { 51 mark[j]=true;c=idx(s[i]); 52 while(j&&!son[j][c])j=fail[j]; 53 j=son[j][c]; 54 if(!mark[j]) 55 for(int tmp=j;tmp;tmp=fail[tmp]) 56 ans+=num[tmp],num[tmp]=0; 57 } 58 printf("%d\n",ans); 59 } 60 }AC;
〖相关题目〗
1.【hdu2222】Keywords Search
题意:给出n个单词和1个模式串,求有多少个单词在模式串中出现。
分析:AC自动机裸题
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e4+5; 7 int T,n,ans; 8 char s[N][55],ss[N*100]; 9 struct Trie 10 { 11 int root,cnt; 12 int son[N*55][26],fail[N*55],num[N*55]; 13 bool mark[N*55]; 14 void init() 15 { 16 cnt=1;root=0; 17 memset(son,0,sizeof(son)); 18 memset(fail,0,sizeof(fail)); 19 memset(num,0,sizeof(num)); 20 memset(mark,0,sizeof(mark)); 21 } 22 int idx(char c){return c-'a';} 23 void ins(char s[]) 24 { 25 int len=strlen(s),cur=root; 26 for(int i=0;i<len;i++) 27 { 28 int id=idx(s[i]); 29 if(!son[cur][id])son[cur][id]=cnt++; 30 cur=son[cur][id]; 31 } 32 num[cur]++; 33 } 34 void build() 35 { 36 int head=0,tail=0,q[N*55],now,nex; 37 for(int i=0;i<26;i++) 38 { 39 now=son[root][i]; 40 if(now)q[tail++]=now; 41 } 42 while(head!=tail) 43 { 44 now=q[head++]; 45 for(int i=0;i<26;i++) 46 { 47 nex=son[now][i]; 48 if(!nex){son[now][i]=son[fail[now]][i];continue;} 49 q[tail++]=nex; 50 fail[nex]=son[fail[now]][i]; 51 } 52 } 53 } 54 void query(char s[]) 55 { 56 int len=strlen(s),j=root,c; 57 for(int i=0;i<len;i++) 58 { 59 mark[j]=true;c=idx(s[i]); 60 while(j&&!son[j][c])j=fail[j]; 61 j=son[j][c]; 62 if(!mark[j]) 63 for(int tmp=j;tmp;tmp=fail[tmp]) 64 ans+=num[tmp],num[tmp]=0; 65 } 66 printf("%d\n",ans); 67 } 68 }AC; 69 void work() 70 { 71 scanf("%d",&n);AC.init();ans=0; 72 for(int i=1;i<=n;i++)scanf("%s",s[i]),AC.ins(s[i]); 73 AC.build();scanf("%s",ss);AC.query(ss); 74 } 75 int main() 76 { 77 scanf("%d",&T); 78 while(T--)work(); 79 return 0; 80 }
2.【hdu2896】病毒侵袭
题意:给出n个单词和m个模式串,输出在每个模式串中出现的单词编号。
分析:AC自动机裸题
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=1e5+5; 6 const int M=1e4+5; 7 int n,m,ans,id[505]; 8 bool mark[N]; 9 char s[M]; 10 struct Trie 11 { 12 int cnt,son[N][128],fail[N],num[N],q[N]; 13 void ins(int v) 14 { 15 int cur=0,len=strlen(s); 16 for(int i=0;i<len;i++) 17 { 18 int now=s[i]; 19 if(!son[cur][now])son[cur][now]=cnt++; 20 cur=son[cur][now]; 21 } 22 num[cur]++;id[v]=cur; 23 } 24 void build() 25 { 26 int head=0,tail=0,now,nex; 27 for(int i=0;i<128;i++) 28 { 29 now=son[0][i]; 30 if(now)q[tail++]=now; 31 } 32 while(head!=tail) 33 { 34 now=q[head++]; 35 for(int i=0;i<128;i++) 36 { 37 nex=son[now][i]; 38 if(!nex){son[now][i]=son[fail[now]][i];continue;} 39 q[tail++]=nex; 40 fail[nex]=son[fail[now]][i]; 41 } 42 } 43 } 44 void query(int v) 45 { 46 int len=strlen(s),j=0,c; 47 bool flag=false; 48 memset(mark,0,sizeof(mark)); 49 for(int i=0;i<len;i++) 50 { 51 c=s[i];j=son[j][c]; 52 for(int tmp=j;tmp;tmp=fail[tmp]) 53 if(num[tmp])flag=true,mark[tmp]=true; 54 } 55 if(!flag)return; 56 printf("web %d:",v); 57 for(int i=1;i<=n;i++) 58 if(mark[id[i]])printf(" %d",i); 59 printf("\n");ans++; 60 } 61 }AC; 62 int main() 63 { 64 AC.cnt=1; 65 scanf("%d",&n); 66 for(int i=1;i<=n;i++)scanf("%s",s),AC.ins(i); 67 AC.build(); 68 scanf("%d",&m); 69 for(int i=1;i<=m;i++)scanf("%s",s),AC.query(i); 70 printf("total: %d",ans); 71 return 0; 72 }
3.【hdu3065】病毒侵袭持续中
题意:给出n个单词和1个模式串,求每个单词在模式串中出现的次数。
分析:AC自动机裸题
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e3+5; 7 const int M=5e4+5; 8 const int L=2e6+5; 9 int n,times[N]; 10 char s[N][55],ch[L]; 11 struct Trie 12 { 13 int cnt,son[M][27],fail[M],num[M],q[M]; 14 void init() 15 { 16 cnt=1; 17 memset(son,0,sizeof(son)); 18 memset(fail,0,sizeof(fail)); 19 memset(num,0,sizeof(num)); 20 } 21 int idx(char c) 22 { 23 if(c>='A'&&c<='Z')return c-'A'; 24 return 26; 25 } 26 void ins(int v) 27 { 28 int cur=0,len=strlen(s[v]); 29 for(int i=0;i<len;i++) 30 { 31 int now=idx(s[v][i]); 32 if(!son[cur][now])son[cur][now]=cnt++; 33 cur=son[cur][now]; 34 } 35 num[cur]=v; 36 } 37 void build() 38 { 39 int head=0,tail=0,now,nex; 40 for(int i=0;i<=26;i++) 41 { 42 now=son[0][i]; 43 if(now)q[tail++]=now; 44 } 45 while(head!=tail) 46 { 47 now=q[head++]; 48 for(int i=0;i<=26;i++) 49 { 50 nex=son[now][i]; 51 if(!nex){son[now][i]=son[fail[now]][i];continue;} 52 q[tail++]=nex; 53 fail[nex]=son[fail[now]][i]; 54 } 55 } 56 } 57 void query() 58 { 59 int len=strlen(ch),j=0,c; 60 for(int i=0;i<len;i++) 61 { 62 c=idx(ch[i]);j=son[j][c]; 63 for(int tmp=j;tmp;tmp=fail[tmp]) 64 if(num[tmp])times[num[tmp]]++; 65 } 66 for(int i=1;i<=n;i++) 67 if(times[i])printf("%s: %d\n",s[i],times[i]); 68 } 69 }AC; 70 int main() 71 { 72 while(scanf("%d",&n)==1) 73 { 74 memset(times,0,sizeof(times)); 75 AC.init(); 76 for(int i=1;i<=n;i++)scanf("%s",s[i]),AC.ins(i); 77 AC.build();scanf("%s",ch);AC.query(); 78 } 79 return 0; 80 }
4.【bzoj2938】病毒
题意:已知某些确定的01串是病毒代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。已知所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
分析:hzwerの博客
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=3e4+5; 6 int n; 7 char s[N]; 8 struct Trie 9 { 10 int cnt,son[N][2],fail[N],q[N]; 11 bool ed[N],in[N],vis[N]; 12 int idx(char c){return c-'0';} 13 void ins() 14 { 15 scanf("%s",s); 16 int cur=0,len=strlen(s); 17 for(int i=0;i<len;i++) 18 { 19 int now=idx(s[i]); 20 if(!son[cur][now])son[cur][now]=cnt++; 21 cur=son[cur][now]; 22 } 23 ed[cur]=true; 24 } 25 void build() 26 { 27 int head=0,tail=0,now,nex; 28 for(int i=0;i<2;i++) 29 { 30 now=son[0][i]; 31 if(now)q[tail++]=now; 32 } 33 while(head!=tail) 34 { 35 now=q[head++]; 36 for(int i=0;i<2;i++) 37 { 38 nex=son[now][i]; 39 if(!nex){son[now][i]=son[fail[now]][i];continue;} 40 q[tail++]=nex; 41 fail[nex]=son[fail[now]][i]; 42 ed[nex]|=ed[fail[nex]]; 43 } 44 } 45 } 46 bool dfs(int x) 47 { 48 in[x]=true; 49 for(int i=0;i<2;i++) 50 { 51 int now=son[x][i]; 52 if(in[now])return true; 53 if(ed[now]||vis[now])continue; 54 vis[now]=true; 55 if(dfs(now))return true; 56 } 57 in[x]=false; 58 return false; 59 } 60 }AC; 61 int main() 62 { 63 AC.cnt=1; 64 scanf("%d",&n); 65 for(int i=1;i<=n;i++)AC.ins(); 66 AC.build(); 67 if(AC.dfs(0))printf("TAK\n"); 68 else printf("NIE\n"); 69 return 0; 70 }
【后缀自动机】
〖注意事项〗
实现后缀排序时,记得t[nq].id=0。
〖模板代码〗
[普通SAM]
1 struct SAM{int mx,fa,ch[26];}t[N<<1]; 2 void ins(int c) 3 { 4 int np=++size; 5 t[np].mx=t[last].mx+1; 6 int x=last;last=np; 7 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 8 if(!x)t[np].fa=root; 9 else 10 { 11 int y=t[x].ch[c]; 12 if(t[y].mx==t[x].mx+1)t[np].fa=y; 13 else 14 { 15 int nq=++size; 16 t[nq]=t[y]; 17 t[nq].mx=t[x].mx+1; 18 t[y].fa=t[np].fa=nq; 19 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 20 } 21 } 22 } 23 int main() 24 { 25 scanf("%s",s+1);n=strlen(s+1); 26 last=size=root=1; 27 for(int i=1;i<=n;i++)ins(s[i]-'a'); 28 return 0; 29 }
[后缀排序]
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #define LL long long 6 using namespace std; 7 const int N=15005; 8 int n,size,root,last,tot,cnt,scnt; 9 int c[26],sa[N],first[N<<1],rk[N<<1]; 10 char ch[N]; 11 bool vis[N<<1]; 12 struct sam{int mx,fa,id,ch[26];}t[N<<1]; 13 struct node{int x,y,v;}a[N<<1]; 14 struct edge{int to,next;}e[N<<1]; 15 void insert(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;} 16 void ins(int c,int pos) 17 { 18 int np=++size; 19 t[np].mx=t[last].mx+1; 20 t[np].id=pos; 21 int x=last;last=np; 22 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 23 if(!x)t[np].fa=root; 24 else 25 { 26 int y=t[x].ch[c]; 27 if(t[y].mx==t[x].mx+1)t[np].fa=y; 28 else 29 { 30 int nq=++size;t[nq]=t[y]; 31 t[nq].mx=t[x].mx+1;t[nq].id=0; 32 t[y].fa=t[np].fa=nq; 33 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 34 } 35 } 36 } 37 void dfs(int x) 38 { 39 if(t[x].id)sa[++scnt]=t[x].id; 40 for(int i=first[x];i;i=e[i].next)dfs(e[i].to); 41 } 42 int main() 43 { 44 scanf("%d%s",&n,ch+1); 45 size=root=last=1;vis[1]=true; 46 for(int i=n;i>=1;i--)ins(ch[i]-'a',i); 47 for(int i=1;i<=size;i++) 48 if(!vis[i]&&t[i].id) 49 for(int pos=n,j=i;!vis[j];vis[j]=true,j=t[j].fa,--pos) 50 { 51 pos=pos-t[j].mx+t[t[j].fa].mx+1; 52 a[++tot]=(node){t[j].fa,j,ch[pos]-'a'}; 53 } 54 for(int i=1;i<=tot;i++)c[a[i].v]++; 55 for(int i=1;i<26;i++)c[i]+=c[i-1]; 56 for(int i=1;i<=tot;i++)rk[c[a[i].v]--]=i; 57 for(int i=tot;i>=1;i--)insert(a[rk[i]].x,a[rk[i]].y); 58 dfs(1); 59 for(int i=1;i<=n;i++)printf("%d\n",sa[i]); 60 return 0; 61 }
〖相关题目〗
1.【Luogu P3804】【模板】后缀自动机
题意:给定一个只包含小写字母的字符串SS,请你求出 SS 的所有出现次数不为 11 的子串的出现次数乘上该子串长度的最大值。
分析:后缀自动机裸题
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e6+5; 7 int n,last,size,root,x; 8 int sz[N<<1],c[N<<1],q[N<<1]; 9 LL ans; 10 char s[N]; 11 struct SAM{int mx,fa,ch[26];}t[N<<1]; 12 void ins(int c) 13 { 14 int np=++size;sz[np]=1; 15 t[np].mx=t[last].mx+1; 16 int x=last;last=np; 17 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 18 if(!x)t[np].fa=root; 19 else 20 { 21 int y=t[x].ch[c]; 22 if(t[y].mx==t[x].mx+1)t[np].fa=y; 23 else 24 { 25 int nq=++size; 26 t[nq]=t[y]; 27 t[nq].mx=t[x].mx+1; 28 t[nq].fa=t[y].fa; 29 t[y].fa=t[np].fa=nq; 30 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 31 } 32 } 33 } 34 int main() 35 { 36 scanf("%s",s+1);n=strlen(s+1); 37 last=size=root=1; 38 for(int i=1;i<=n;i++)ins(s[i]-'a'); 39 for(int i=1;i<=size;i++)c[t[i].mx]++; 40 for(int i=1;i<=size;i++)c[i]+=c[i-1]; 41 for(int i=1;i<=size;i++)q[c[t[i].mx]--]=i; 42 for(int i=size;i>=1;i--) 43 { 44 x=q[i];sz[t[x].fa]+=sz[x]; 45 if(sz[x]>1)ans=max(ans,1ll*sz[x]*t[x].mx); 46 } 47 printf("%lld",ans); 48 return 0; 49 }
2.【bzoj3238】[Ahoi2013]差异
题意:给定长度为n的小写字母字符串,令Ti表示以i开头的后缀,求Σ[Ti+Tj-2*lcp(Ti,Tj)],1<=i<j<=n。
分析:把串倒过来,两个后缀的lcp就是他们在Parent树上的LCA
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e6+5; 7 int n,size,last,root,cnt; 8 int first[N],sz[N]; 9 LL ans; 10 bool f[N]; 11 char s[N]; 12 struct SAM{int mx,fa,ch[26];}t[N]; 13 struct edge{int to,next;}e[N]; 14 void insert(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;} 15 void ins(int c) 16 { 17 int np=++size;f[np]=true; 18 t[np].mx=t[last].mx+1; 19 int x=last;last=np; 20 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 21 if(!x)t[np].fa=root; 22 else 23 { 24 int y=t[x].ch[c]; 25 if(t[y].mx==t[x].mx+1)t[np].fa=y; 26 else 27 { 28 int nq=++size; 29 t[nq]=t[y]; 30 t[nq].mx=t[x].mx+1; 31 t[y].fa=t[np].fa=nq; 32 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 33 } 34 } 35 } 36 void dfs(int x) 37 { 38 sz[x]=f[x]?1:0; 39 for(int i=first[x];i;i=e[i].next) 40 { 41 int to=e[i].to;dfs(to); 42 ans+=1ll*t[x].mx*sz[x]*sz[to]; 43 sz[x]+=sz[to]; 44 } 45 } 46 int main() 47 { 48 scanf("%s",s+1);n=strlen(s+1); 49 last=size=root=1; 50 for(int i=n;i>=1;i--)ins(s[i]-'a'); 51 for(int i=2;i<=size;i++)insert(t[i].fa,i); 52 dfs(1);printf("%lld",1ll*(n+1)*n/2*(n-1)-2*ans); 53 return 0; 54 }
3.【bzoj4032】[HEOI2015]最短不公共子串
题意:给两个小写字母串A,B,计算:(1) A的一个最短的子串,它不是B的子串;(2) A的一个最短的子串,它不是B的子序列;(3) A的一个最短的子序列,它不是B的子串;(4) A的一个最短的子序列,它不是B的子序列。
分析:后缀自动机+序列自动机
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=2005; 7 const int inf=0x3f3f3f3f; 8 int n,m,c,x,y,root,size,last,ans,now,sum; 9 int pre[26],nexa[N][26],nexb[N][26],f[N][N<<1]; 10 char a[N],b[N]; 11 struct SAM{int mx,fa,ch[26];}t[N<<1]; 12 void insert(int c) 13 { 14 int np=++size; 15 t[np].mx=t[last].mx+1; 16 int x=last;last=np; 17 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 18 if(!x)t[np].fa=root; 19 else 20 { 21 int y=t[x].ch[c]; 22 if(t[y].mx==t[x].mx+1)t[np].fa=y; 23 else 24 { 25 int nq=++size;t[nq]=t[y]; 26 t[nq].mx=t[x].mx+1; 27 t[y].fa=t[np].fa=nq; 28 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 29 } 30 } 31 } 32 void work1() 33 { 34 ans=n+1; 35 for(int i=1;i<=n;i++) 36 { 37 now=root;sum=0;c=a[i+sum]-'a'; 38 while(i+sum<=n&&t[now].ch[c]) 39 { 40 now=t[now].ch[c];sum++; 41 c=a[i+sum]-'a'; 42 } 43 if(i+sum!=n+1)ans=min(ans,sum+1); 44 } 45 if(ans==n+1)printf("-1\n"); 46 else printf("%d\n",ans); 47 } 48 void work2() 49 { 50 ans=n+1; 51 for(int i=1;i<=n;i++) 52 { 53 now=0;sum=0;c=a[i+sum]-'a'; 54 while(i+sum<=n&&nexb[now][c]) 55 { 56 now=nexb[now][c];sum++; 57 c=a[i+sum]-'a'; 58 } 59 if(i+sum!=n+1)ans=min(ans,sum+1); 60 } 61 if(ans==n+1)printf("-1\n"); 62 else printf("%d\n",ans); 63 } 64 void work3() 65 { 66 memset(f,0x3f,sizeof(f)); 67 f[0][1]=0;ans=n+1; 68 for(int i=0;i<n;i++) 69 for(int j=1;j<=size;j++) 70 { 71 if(f[i][j]==inf)continue; 72 for(int k=0;k<26;k++) 73 if(x=nexa[i][k]) 74 { 75 y=t[j].ch[k]; 76 if(!y)ans=min(ans,f[i][j]+1); 77 else f[x][y]=min(f[x][y],f[i][j]+1); 78 } 79 } 80 if(ans==n+1)printf("-1\n"); 81 else printf("%d\n",ans); 82 } 83 void work4() 84 { 85 memset(f,0x3f,sizeof(f)); 86 f[0][0]=0;ans=n+1; 87 for(int i=0;i<n;i++) 88 for(int j=0;j<=m;j++) 89 { 90 if(f[i][j]==inf)continue; 91 for(int k=0;k<26;k++) 92 if(x=nexa[i][k]) 93 { 94 y=nexb[j][k]; 95 if(!y)ans=min(ans,f[i][j]+1); 96 else f[x][y]=min(f[x][y],f[i][j]+1); 97 } 98 } 99 if(ans==n+1)printf("-1\n"); 100 else printf("%d\n",ans); 101 } 102 int main() 103 { 104 scanf("%s%s",a+1,b+1); 105 n=strlen(a+1);m=strlen(b+1); 106 last=root=size=1; 107 for(int i=1;i<=m;i++)insert(b[i]-'a'); 108 for(int i=1;i<=n;i++) 109 { 110 c=a[i]-'a'; 111 for(int j=i-1;j>=pre[c];j--)nexa[j][c]=i; 112 pre[c]=i; 113 } 114 memset(pre,0,sizeof(pre)); 115 for(int i=1;i<=m;i++) 116 { 117 c=b[i]-'a'; 118 for(int j=i-1;j>=pre[c];j--)nexb[j][c]=i; 119 pre[c]=i; 120 } 121 work1();work2();work3();work4(); 122 return 0; 123 }
4.【bzoj3998】[TJOI2015]弦论
题意:对于一个给定长度为N的字符串,求它的第K小子串是什么。
分析:hzwerの博客
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=5e5+5; 7 int n,root,last,size,T,K,x; 8 int c[N<<1],val[N<<1],q[N<<1],sum[N<<1]; 9 char s[N]; 10 struct SAM{int mx,fa,ch[26];}t[N<<1]; 11 void insert(int c) 12 { 13 int np=++size;val[np]=1; 14 t[np].mx=t[last].mx+1; 15 int x=last;last=np; 16 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 17 if(!x)t[np].fa=root; 18 else 19 { 20 int y=t[x].ch[c]; 21 if(t[y].mx==t[x].mx+1)t[np].fa=y; 22 else 23 { 24 int nq=++size; 25 t[nq]=t[y]; 26 t[nq].mx=t[x].mx+1; 27 t[y].fa=t[np].fa=nq; 28 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 29 } 30 } 31 } 32 void dfs(int now,int K) 33 { 34 if(K<=val[now])return; 35 K-=val[now]; 36 for(int i=0;i<26;i++) 37 { 38 x=t[now].ch[i]; 39 if(!x)continue; 40 if(K<=sum[x]){putchar(i+'a');dfs(x,K);return;} 41 K-=sum[x]; 42 } 43 } 44 int main() 45 { 46 scanf("%s%d%d",s+1,&T,&K); 47 n=strlen(s+1);root=last=size=1; 48 for(int i=1;i<=n;i++)insert(s[i]-'a'); 49 for(int i=1;i<=size;i++)c[t[i].mx]++; 50 for(int i=1;i<=size;i++)c[i]+=c[i-1]; 51 for(int i=1;i<=size;i++)q[c[t[i].mx]--]=i; 52 for(int i=size;i>=1;i--) 53 { 54 x=q[i]; 55 if(T==1)val[t[x].fa]+=val[x]; 56 else val[x]=1; 57 } 58 val[1]=0; 59 for(int i=size;i>=1;i--) 60 { 61 x=q[i];sum[x]=val[x]; 62 for(int j=0;j<26;j++)sum[x]+=sum[t[x].ch[j]]; 63 } 64 if(K>sum[root]){printf("-1\n");return 0;} 65 dfs(root,K); 66 return 0; 67 }
5.【bzoj2780】[Spoj]8093 Sevenk Love Oimaster
题意:给你n个文本串,m个询问串,询问每个串在多少个文本串中出现过。
分析:广义后缀自动机。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 using namespace std; 6 const int N=1e4+5; 7 const int M=1e5+5; 8 int n,m,last,root,size,now,len,tmp; 9 int L[N],R[N],vis[M<<1],sz[M<<1]; 10 char str[M]; 11 struct SAM{int fa,mx,ch[26];}t[M<<1]; 12 void insert(int c) 13 { 14 int np=++size; 15 t[np].mx=t[last].mx+1; 16 int x=last;last=np; 17 while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa; 18 if(!x)t[np].fa=root; 19 else 20 { 21 int y=t[x].ch[c]; 22 if(t[y].mx==t[x].mx+1)t[np].fa=y; 23 else 24 { 25 int nq=++size; 26 t[nq]=t[y]; 27 t[nq].mx=t[x].mx+1; 28 t[y].fa=t[np].fa=nq; 29 while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa; 30 } 31 } 32 } 33 void up(int x,int id) 34 { 35 while(x&&vis[x]!=id) 36 { 37 sz[x]++;vis[x]=id; 38 x=t[x].fa; 39 } 40 } 41 int main() 42 { 43 last=root=size=1; 44 scanf("%d%d",&n,&m); 45 for(int i=1;i<=n;i++) 46 { 47 L[i]=R[i-1];scanf("%s",str+L[i]); 48 R[i]=strlen(str);last=1; 49 for(int j=L[i];j<R[i];j++)insert(str[j]-'a'); 50 } 51 for(int i=1;i<=n;i++) 52 { 53 now=1; 54 for(int j=L[i];j<R[i];j++) 55 now=t[now].ch[str[j]-'a'],up(now,i); 56 } 57 int i; 58 while(m--) 59 { 60 scanf("%s",str); 61 len=strlen(str);now=1; 62 for(i=0;i<len;i++) 63 { 64 tmp=t[now].ch[str[i]-'a']; 65 if(!tmp)break;now=tmp; 66 } 67 if(i==len)printf("%d\n",sz[now]); 68 else printf("0\n"); 69 } 70 return 0; 71 }
【序列自动机】
〖模板代码〗
1 for(int i=1;i<=n;i++) 2 { 3 c=s[i]-'a'; 4 for(int j=i-1;j>=pre[c];j--)nex[j][c]=i; 5 pre[c]=i; 6 }
【Manacher】
〖模板代码〗
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #define LL long long 6 using namespace std; 7 const int N=2e7+2e6+5; 8 int n,len,ans,p[N]; 9 char ch[N],s[N]; 10 int main() 11 { 12 scanf("%s",ch+1); 13 len=strlen(ch+1); 14 s[0]='!';s[1]='#';n=1; 15 for(int i=1;i<=len;i++) 16 s[++n]=ch[i],s[++n]='#'; 17 int mxr=0,id=0; 18 for(int i=1;i<=n;i++) 19 { 20 if(i<mxr)p[i]=min(p[2*id-i],mxr-i); 21 else p[i]=1; 22 for(;i+p[i]<=n&&s[i-p[i]]==s[i+p[i]];p[i]++); 23 if(i+p[i]>mxr)mxr=i+p[i],id=i; 24 ans=max(ans,p[i]-1); 25 } 26 printf("%d",ans); 27 return 0; 28 }
〖相关题目〗
1.【bzoj2342】[Shoi2011]双倍回文
题意:见原题
分析:hzwerの博客
1 #include<algorithm> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstring> 5 #include<set> 6 #define LL long long 7 using namespace std; 8 const int N=1e6+5; 9 int len,n,ans,p[N],f[N]; 10 char ch[N],s[N]; 11 set<int>bt; 12 struct node{int x,id;}a[N]; 13 bool cmp(node a,node b){return a.x<b.x;} 14 void manacher() 15 { 16 int mxr=0,id=0; 17 for(int i=1;i<=n;i++) 18 { 19 if(i<mxr)p[i]=min(p[2*id-i],mxr-i);else p[i]=1; 20 for(;i+p[i]<=n&&s[i-p[i]]==s[i+p[i]];p[i]++); 21 if(i+p[i]>mxr)mxr=i+p[i],id=i; 22 } 23 } 24 int main() 25 { 26 scanf("%d%s",&len,ch+1); 27 s[0]='!';s[1]='#';n=1; 28 for(int i=1;i<=len;i++)s[++n]=ch[i],s[++n]='#'; 29 manacher(); 30 for(int i=1;i<=len;i++)f[i]=p[(i<<1)|1]-1,f[i]>>=1; 31 for(int i=1;i<=len;i++)a[i].id=i,a[i].x=i-f[i]; 32 sort(a+1,a+len+1,cmp); 33 int id=0; 34 for(int i=1;i<=len;i++) 35 { 36 while(id+1<=n&&a[id+1].x<=i)id++,bt.insert(a[id].id); 37 set<int>::iterator now=bt.upper_bound(i+f[i]/2); 38 if(now!=bt.begin())ans=max(ans,((*--now)-i)*4); 39 } 40 printf("%d\n",ans); 41 return 0; 42 }