首先:
jxrjxrjxr Orz,没有您我们都会死~
然后就是我从jxr神犇那里借鉴(照抄)过来的后缀数组模板。
#include<iostream> #include<cstdio> #include<cstring> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=100000+1000; char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); }真是十分的 短 啊~~~~~~
话说我现在才会后缀数组真的好吗,简直违反基本法啊。
不过还是稀里糊涂地看懂(背下来)了。
于是就可以开心地去水题了。
没错其实我就是把09年那篇论文里面的题给水了下。
然后来写下一句话题解。
POJ1743:不可重叠最长重复子串
二分答案,height分组,维护最大最小sa,判定一下。
<span style="font-size:14px;">#include<iostream> #include<cstdio> #include<cstring> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=20000+5; int s[N],a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; int abs(int x){ return max(x,-x); } void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return ; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } bool check(int k){ int _min=sa[1],_max=sa[1]; for(int i=2;i<=n+1;i++){ if(i==n+1||h[i]<k){ if(_max-_min>=k)return true; _max=_min=sa[i]; }else{ _min=min(_min,sa[i]); _max=max(_max,sa[i]); } } return false; } int main(){ while(scanf("%d",&n)&&n){ for(int i=1;i<=n;i++)scanf("%d",&s[i]); for(int i=1;i<n;i++)s[i]=s[i+1]-s[i]+100; n--; x=a;y=b; getsa(200);geth(); int l=0,r=n/2; while(l<=r){ int mid=l+r>>1; if(check(mid))l=mid+1; else r=mid-1; } if(l>=5)printf("%d\n",l); else puts("0"); } return 0; }</span>
sigma(n-sa[i]+1-height[i])
<span style="font-size:14px;">#include<iostream> #include<cstdio> #include<cstring> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=1000+5; char s[N]; int sa[N],h[N],rk[N],a[N],b[N],c[N],*x,*y,n; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } int main(){ int T;scanf("%d",&T); while(T--){ scanf("%s",s+1); n=strlen(s+1); x=a;y=b; getsa(200);geth(); int ans=0; for(int i=1;i<=n;i++) ans+=n-sa[i]+1-h[i]; printf("%d\n",ans); } return 0; }</span>
枚举长度l,计算次数,复杂度是n*调和级数(n),中间细节比较麻烦。。。。。
<span style="font-size:14px;">#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=100000+10; struct Suffix_Array{ char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; int st[N][20]; void init(char *t){ n=strlen(t+1); for(int i=1;i<=n;i++)s[i]=t[i]; x=a;y=b; } void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } void rmq_init(){ for(int i=1;i<=n;i++)st[i][0]=h[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int rmq(int l,int r){ int k=log2(r-l+1); return min(st[l][k],st[l+(1<<k)-1][k]); } int query(int i,int j){ int l=rk[i],r=rk[j]; if(l>r)swap(l,r); return rmq(l+1,r); } void pre(){ getsa(200);geth();rmq_init(); } }sol; char s[N]; int a[N]; int main(){ int kase=0; while(true){ scanf("%s",s+1); if(s[1]=='#')break; int n=strlen(s+1); sol.init(s);sol.pre(); int ans=0,top=0; for(int l=1;l<=n;l++) for(int i=1;i+l<=n;i+=l){ int r=sol.query(i,i+l); int ti=r/l+1; int k=i-(l-r%l); if(k>=1&&r%l) if(sol.query(k,k+l)>=r)ti++; if(ti>ans){ ans=ti; a[top=1]=l; }else if(ti==ans)a[++top]=l; } int len=-1,st; for(int i=1;i<=n&&len==-1;i++) for(int j=1;j<=top;j++){ int l=a[j]; if(sol.query(sol.sa[i],sol.sa[i]+l)>=(ans-1)*l){ len=l; st=sol.sa[i]; break; } } printf("Case %d: ",++kase); for(int i=st,j=1;j<=len*ans;j++,i++)putchar(s[i]); putchar('\n'); } return 0; }</span>
两个串中间加个奇怪的符号然后连接起来,跑一遍后缀数组,当sa[i]和sa[i-1]不在一个串时的最大height[i]即为答案。
<span style="font-size:14px;">#include<iostream> #include<cstdio> #include<cstring> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=200000+10; char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)break; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } int main(){ scanf("%s",s+1); int m=strlen(s+1); s[m+1]='$'; scanf("%s",s+m+2); n=strlen(s+1); x=a;y=b; getsa(200);geth(); int ans=0; for(int i=2;i<=n;i++) if(sa[i-1]<=m^sa[i]<=m)ans=max(ans,h[i]); printf("%d",ans); return 0; }</span>
POJ 3415:长度不小于k的公共子串个数
继续串接,跑一遍后缀数组,对于每一个A的后缀和B的后缀计算一次LCP算出答案,于是N^2爆炸,转用单调栈维护A的height,扫到B的时候算一下,反过来再做一次,得出总答案。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) const int N=200000+5; char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } int st[N][2]; int main(){ int k; while(scanf("%d",&k)&&k){ scanf("%s",s+1); int m=strlen(s+1); s[++m]='$'; scanf("%s",s+m+1); n=strlen(s+1); x=a;y=b; getsa(200);geth(); ll sum=0,tot=0,top=0; for(int i=2;i<=n+1;i++){ if(h[i]<k)tot=top=0; else{ int cnt=0; if(sa[i-1]<m)cnt++,tot+=h[i]-k+1; while(top&&h[i]<=st[top-1][0]){ top--; tot-=st[top][1]*(st[top][0]-h[i]); cnt+=st[top][1]; } st[top][0]=h[i];st[top++][1]=cnt; if(sa[i]>m)sum+=tot; } } tot=top=0; for(int i=2;i<=n+1;i++){ if(h[i]<k)tot=top=0; else{ int cnt=0; if(sa[i-1]>m)cnt++,tot+=h[i]-k+1; while(top>0&&h[i]<=st[top-1][0]){ top--; tot-=st[top][1]*(st[top][0]-h[i]); cnt+=st[top][1]; } st[top][0]=h[i];st[top++][1]=cnt; if(sa[i]<m)sum+=tot; } } printf("%lld\n",sum); } return 0; }
这题其实可以KMP做我会乱说= =+(常数我吃了)。
n个串连起来,中间有奇怪的不同的字符,跑一遍后缀数组,二分答案,height分组,每组判定一下有木有集齐(7个后缀召唤神龙?)k个不在同一个串的后缀,集齐了就可以(召唤神龙)return true
顺便说这题召唤出了我人生中的第一个OLE(我也不造怎么回事啊,然后莫名其妙就A了)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) const int N=100000+1000; char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],*x,*y,n; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } int in[N],T,k,now[105]; bool check(int len){ int tot=0; T=1; memset(now,0,sizeof(now)); for(int i=1;i<=n;i++){ if(h[i]<len){ if(tot>k)return true; tot=1; now[in[sa[i]]]=++T; } else{ if(now[in[sa[i]]]!=T){ now[in[sa[i]]]=T; tot++; } } } if(tot>k)return true; return false; } int ans[N]; void print(int len){ int tot=0,sz=0; T=1; memset(now,0,sizeof(now)); for(int i=1;i<=n;i++){ if(h[i]<len){ if(tot>k)ans[++sz]=sa[i-1]; tot=1; now[in[sa[i]]]=++T; } else{ if(now[in[sa[i]]]!=T){ now[in[sa[i]]]=T; tot++; } } } if(tot>k)ans[++sz]=sa[n]; ans[0]=sz; } int main(){ int m;bool flag=false; while(scanf("%d",&m)&&m){ memset(s,0,sizeof(s)); int tmp=1,top=1; int l=0,r=1005; for(int i=1;i<=m;i++){ scanf("%s",s+tmp); int len=strlen(s+tmp); r=min(r,len); for(int j=tmp;j<tmp+len;j++) in[j]=i; s[tmp+=len]=top++; tmp++; } n=strlen(s+1); x=a;y=b; getsa(300);geth(); k=m/2; while(l<=r){ int mid=l+r>>1; if(check(mid))l=mid+1; else r=mid-1; } if(flag)puts(""); flag=true; if(l==1)puts("?"); else{ print(l-1); for(int i=1;i<=ans[0];i++){ for(int j=ans[i];j<ans[i]+l-1;j++) putchar(s[j]); putchar('\n'); } } } return 0; }
其实。。。。。这题跟上题差不多,那个至少出现两次并无卵用,因为判断的时候只要串长不为0,出现1次的都干掉了。
串接,二分答案,height分组(TM还能有点别的?)然后维护mx和mn,即最大最小sa值(跟第1题的不可重叠一样的)。
#include<iostream> #include<cstdio> #include<cstring> #define cmp(x) (y[sa[i]+x]==y[sa[i-1]+x]) using namespace std; const int N=100000+1000; char s[N]; int a[N],b[N],c[N],sa[N],rk[N],h[N],n,*x,*y; void radix(int m){ for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=n;i++)c[x[y[i]]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i]; } void getsa(int m){ for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i; radix(m); for(int k=1,p=0;k<=n;k<<=1,m=p,p=0){ for(int i=n-k+1;i<=n;i++)y[++p]=i; for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k; radix(m);swap(x,y);x[sa[1]]=p=1; for(int i=2;i<=n;i++) x[sa[i]]=cmp(0)&&cmp(k)?p:++p; if(p==n)return; } } void geth(){ for(int i=1;i<=n;i++)rk[sa[i]]=i; memset(h,0,sizeof(h)); for(int i=1,k=0;i<=n;h[rk[i++]]=k) for(k?k--:1;i+k<=n&&s[i+k]==s[sa[rk[i]-1]+k];k++); } int in[N],mx[15],mn[15],m; bool check(int len){ memset(mx,0,sizeof(mx)); memset(mn,0x3f,sizeof(mn)); for(int i=2;i<=n+1;i++){ if(h[i]<len){ memset(mx,0,sizeof(mx)); memset(mn,0x3f,sizeof(mn)); mx[in[sa[i]]]=sa[i]; mn[in[sa[i]]]=sa[i]; }else{ mx[in[sa[i]]]=max(mx[in[sa[i]]],sa[i]); mn[in[sa[i]]]=min(mn[in[sa[i]]],sa[i]); mx[in[sa[i-1]]]=max(mx[in[sa[i-1]]],sa[i-1]); mn[in[sa[i-1]]]=min(mn[in[sa[i-1]]],sa[i-1]); int j; for(j=1;j<=m;j++) if(mx[j]-mn[j]<len)break; if(j>m)return true; } } return false; } int main(){ int T;scanf("%d",&T); while(T--){ scanf("%d",&m); n=1; int l=0,r=10000; for(int i=1;i<=m;i++){ scanf("%s",s+n); int len=strlen(s+n); for(int j=n;j<n+len;j++){ in[j]=i; s[j]-='a'-1; } s[n+=len]=i+26; n++; } n--; x=a;y=b; getsa(200);geth(); int ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)){l=mid+1;ans=mid;} else r=mid-1; } printf("%d\n",ans); } return 0; }
然后其实还有一个最小循环节和一个最长回文串,其实这两个用KMP和Manacher做就好了嘛,还快一点呢。
总结一下嘛,这类题无非就是 串接,二分答案,height分组,偶尔再来个单调栈什么的,其实做法都差不多,模型很相像,理解了height数组和sa数组的定义啊性质啊什么的就很好做(水)了。
好了是时候开启后缀自动机的大门了(还是好虚啊肿么办)