后缀数组果然是个神奇的东西…看起来非常巧妙的样子。话说我花了很长时间终于把罗穗骞大神论文里的代码弄懂了。。。
论文在这里:
http://wenku.baidu.com/view/ed1be61e10a6f524ccbf85fd.html
(OrzOrzOrzOrz)
关于后缀数组的一些小应用:
给定一个字符串,求重复次数最多的连续重复子串。如果存在多个,则输出字典序最小的一个。
枚举长度 l,然后求长度为 l 的子串最多能连续出现几次。
http://www.cnblogs.com/wangziyun/archive/2013/03/18/2966628.html
给定一列数,求最长重复子串,且这两个子串不能重叠。
注意:这道题中的“重复”定义为整个子串加上或减去一个整数后和另一子串相同。
转化为差分序列后二分答案 k,然后把height数组分组,每组的后缀之间的height值不少于 k。判断每组中sa的最大值与最小值之差是否不小于 k。如果有一组满足,那么该答案就是满足条件的。这种分组的思想似乎十分常用。
View Code1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 using namespace std; 5 #define maxn 20010 6 #define inf 0x3f3f3f3f 7 8 int r[maxn],wa[maxn],wb[maxn],ws[maxn],wv[maxn],h[maxn],rank[maxn],sa[maxn],n,m; 9 void calc(int n) 10 { 11 int i,j,k=0; 12 for (i=1;i<=n;i++) rank[sa[i]]=i; 13 for (i=1;i<=n;h[rank[i++]]=k) 14 for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 15 } 16 bool cmp(int *r,int i,int j,int l) 17 { 18 return r[i]==r[j] && r[i+l]==r[j+l]; 19 } 20 void da(int n) 21 { 22 int i,j,p,*x=wa,*y=wb,*t; 23 for (i=0;i<=m;i++) ws[i]=0; 24 for (i=1;i<=n;i++) ws[x[i]=r[i]]++; 25 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 26 for (i=n;i;i--) sa[ws[x[i]]--]=i; 27 for (j=p=1;p<=n;m=p-1,j<<=1) 28 { 29 for (p=1,i=n-j+1;i<=n;i++) y[p++]=i; 30 for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j; 31 for (i=0;i<=m;i++) ws[i]=0; 32 for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++; 33 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 34 for (i=n;i;i--) sa[ws[wv[i]]--]=y[i]; 35 for (t=x,x=y,y=t,i=p=2,x[sa[1]]=1;i<=n;i++) 36 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 37 } 38 for (i=1;i<n;i++) sa[i]=sa[i+1]; 39 calc(n-1); 40 } 41 bool check(int k) 42 { 43 int min=inf,max=0; 44 for (int i=1;i<=n;i++) 45 { 46 if (h[i]>=k) 47 { 48 if (sa[i]>max) max=sa[i]; 49 if (sa[i]<min) min=sa[i]; 50 if (sa[i-1]>max) max=sa[i-1]; 51 if (sa[i-1]<min) min=sa[i-1]; 52 if (max-min>=k) return 1; 53 } 54 else min=inf,max=0; 55 } 56 return 0; 57 } 58 59 int main() 60 { 61 scanf("%d",&n); 62 while(n) 63 { 64 m=0; 65 for (int i=1;i<=n;i++) scanf("%d",&r[i]); 66 for (int i=1;i<n;i++) r[i]=r[i+1]-r[i]; 67 for (int i=1;i<n;i++) if (r[i]<m) m=r[i]; 68 for (int i=1;i<n;i++) r[i]-=(m-1); 69 for (int i=1;i<n;i++) if (r[i]>m) m=r[i]; 70 r[n]=0; 71 da(n); 72 int l=1,r=n,mid; 73 while (l!=r-1) 74 { 75 mid=(l+r)>>1; 76 if (check(mid)) l=mid; 77 else r=mid; 78 if (r<4) break; 79 } 80 if (l<4) printf("0\n"); 81 else printf("%d\n",l+1); 82 scanf("%d",&n); 83 } 84 return 0; 85 }
给定一列数,求最长的连续重复至少k次的子串,这k个子串可以重复。
同样二分答案 k,把height数组分组判定每一组中的后缀个数是否不少于 k。
View Code1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 using namespace std; 5 #define maxn 20010 6 7 int wa[maxn],wb[maxn],ws[maxn],wv[maxn],r[maxn],n,m,h[maxn],sa[maxn],rank[maxn],k; 8 9 bool cmp(int *r,int i,int j,int l) 10 { 11 return r[i]==r[j] && r[i+l]==r[j+l]; 12 } 13 void calc(int n) 14 { 15 int i,j,k=0; 16 for (i=1;i<=n;i++) rank[sa[i]]=i; 17 for (i=1;i<=n;h[rank[i++]]=k) 18 for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 19 } 20 void da(int n,int m) 21 { 22 int *x=wa,*y=wb,*t,p,i,j; 23 for (i=0;i<=m;i++) ws[i]=0; 24 for (i=1;i<=n;i++) ws[x[i]=r[i]]++; 25 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 26 for (i=n;i;i--) sa[ws[x[i]]--]=i; 27 for (j=1,p=1;p<n;m=p,j*=2) 28 { 29 for (p=1,i=n-j+1;i<=n;i++) y[p++]=i; 30 for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j; 31 for (i=0;i<=m;i++) ws[i]=0; 32 for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++; 33 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 34 for (i=n;i;i--) sa[ws[wv[i]]--]=y[i]; 35 for (t=x,x=y,y=t,x[sa[1]]=1,i=2,p=1;i<=n;i++) 36 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p; 37 } 38 for (i=1;i<=n;i++) sa[i]=sa[i+1]; 39 calc(n-1); 40 } 41 bool check(int x) 42 { 43 int cnt=0; 44 for (int i=1;i<=n;i++) 45 { 46 if (h[i]>=x) 47 { 48 cnt++; 49 if (cnt>=k-1) return 1; 50 } 51 else cnt=0; 52 } 53 return 0; 54 } 55 56 int main() 57 { 58 scanf("%d%d",&n,&k); 59 for (int i=1;i<=n;i++) scanf("%d",&r[i]); 60 for (int i=1;i<=n;i++) if (r[i]>m) m=r[i]; 61 r[n+1]=0; 62 da(n+1,m); 63 int l=1,r=n,mid; 64 while (l!=r-1) 65 { 66 mid=(l+r)>>1; 67 if (check(mid)) l=mid; 68 else r=mid; 69 } 70 printf("%d\n",l); 71 return 0; 72 }
给定两个字符串,求长度不小于 k 的公共子串的个数。
将两个字符串连起来,中间用一个特殊字符隔开,按height数组分组后,依次扫描,每扫到一个后缀,就统计这一组中所有与它不在同一子串中的后缀产生的长度不小于 k 的公共子串的个数。可以用一个单调栈来维护。具体的可以看这里:http://hi.baidu.com/wplzzz/item/224f91ee33c8a1355b2d64a5。这篇文章讲得非常详细。
View Code1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 using namespace std; 6 #define maxn 200010 7 8 int w1[maxn],w2[maxn],ws[maxn],wv[maxn],h[maxn],sa[maxn],rank[maxn],r[maxn],n,m,l1,l2; 9 bool cmp(int *r,int i,int j,int l) 10 { 11 return r[i]==r[j] && r[i+l]==r[j+l]; 12 } 13 void calc(int n) 14 { 15 int i,j,k=0; 16 for (i=1;i<=n;i++) rank[sa[i]]=i; 17 for (i=1;i<=n;h[rank[i++]]=k) 18 for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 19 } 20 void da(int n,int m) 21 { 22 int i,j,p,*x=w1,*y=w2,*t; 23 for (i=0;i<=m;i++) ws[i]=0; 24 for (i=1;i<=n;i++) ws[x[i]=r[i]]++; 25 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 26 for (i=n;i;i--) sa[ws[x[i]]--]=i; 27 for (j=1,p=1;p<=n;m=p,j<<=1) 28 { 29 for (p=1,i=n-j+1;i<=n;i++) y[p++]=i; 30 for (i=1;i<=n;i++) if (sa[i]>j) y[p++]=sa[i]-j; 31 for (i=0;i<=m;i++) ws[i]=0; 32 for (i=1;i<=n;i++) ws[wv[i]=x[y[i]]]++; 33 for (i=1;i<=m;i++) ws[i]+=ws[i-1]; 34 for (i=n;i;i--) sa[ws[wv[i]]--]=y[i]; 35 for (t=x,x=y,y=t,i=2,p=2,x[sa[1]]=1;i<=n;i++) 36 x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 37 } 38 for (i=1;i<n;i++) sa[i]=sa[i+1]; 39 calc(n-1); 40 } 41 char c[maxn],t[maxn>>1]; 42 struct stack 43 { 44 int v,wa,wb; 45 }st[maxn]; 46 int top; 47 48 long long solve(int len) 49 { 50 long long ans=0,sum1=0,sum0=0; 51 for (int i=1;i<=len;i++) h[i]=max(0,h[i]-n+1); 52 for (int i=1;i<=len;i++) 53 { 54 if (sa[i-1]<=l1) 55 { 56 st[top+1].wa=1; 57 st[top+1].wb=0; 58 sum0+=h[i]; 59 } 60 else 61 { 62 st[top+1].wb=1; 63 st[top+1].wa=0; 64 sum1+=h[i]; 65 } 66 st[top+1].v=h[i]; 67 while (st[top+1].v<=st[top].v && top) 68 { 69 sum0-=(st[top].v-st[top+1].v)*st[top].wa; 70 sum1-=(st[top].v-st[top+1].v)*st[top].wb; 71 st[top].v=st[top+1].v; 72 st[top].wa+=st[top+1].wa; 73 st[top].wb+=st[top+1].wb; 74 top--; 75 } 76 top++; 77 if (sa[i]<=l1) ans+=sum1; 78 else ans+=sum0; 79 } 80 return ans; 81 } 82 83 int main() 84 { 85 while (~scanf("%d",&n)) 86 { 87 if (!n) break; 88 memset(sa,0,sizeof(sa)); 89 memset(h,0,sizeof(h)); 90 memset(rank,0,sizeof(rank)); 91 int len=1; 92 m=0; 93 scanf("%s",c+1); 94 scanf("%s",t+1); 95 for (;c[len];len++) r[len]=c[len]; 96 l1=len-1; 97 r[len++]=128; 98 for (;t[len-l1-1];len++) r[len]=t[len-l1-1]; 99 l2=len-l1-2; 100 r[len]=0; 101 for (int i=1;i<=len;i++) m=(r[i]>m)?r[i]:m; 102 da(len,m); 103 len--; 104 printf("%lld\n",solve(len)); 105 } 106 return 0; 107 }
我是弱菜,刷得太少了。。。
剩下的题请看 stick_jitb 大神的博客: http://www.cnblogs.com/stickjitb/archive/2013/03/23/2976530.html (Orz Orz Orz)
来源:https://www.cnblogs.com/wangziyun/archive/2013/03/20/2971955.html