后缀数组果然是个神奇的东西…看起来非常巧妙的样子。话说我花了很长时间终于把罗穗骞大神论文里的代码弄懂了。。。
论文在这里:
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 Code
1 #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 Code
1 #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 Code
1 #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
