POJ - 3693 Maximum repetition substring 后缀数组+RMQ
Posted 卷珠帘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ - 3693 Maximum repetition substring 后缀数组+RMQ相关的知识,希望对你有一定的参考价值。
http://poj.org/problem?id=3693
整体思路就是枚举长度L,看长度为L的字符串在s中连续出现了几次。
既然长度为L的串是重复出现的,那么s[0]…s[L]…s[2*L]…中相邻的两个一定出现在重复的L串中。(并不一定在首尾位置,也可能出现在中间)。
那么我们求i*L和(i+1)*L的LCP,假设答案为M,那么答案应该是M/L+1(加一是因为i*L~(i+1)*L也是答案)。
但是这个答案不一定是最优,举一个下面的例子。
此时M/L+1是3,而正确答案是4。为什么呢?是因为我们枚举的相邻位置不一定在首尾位置。M%L可以理解为末尾多出的字符,我们更可以理解为是前面少了L-M%L个字符,我们把起始位置挪前,挪到(i*L-(L-M%L))位置,同理,(i+1)*L也挪。
保证字典序最小,我们只要将sa数组从前往后枚举即可。
这题还学会了这样的复杂度的大小
1 #include <iostream>
2 #include <cstdio>
3 #include <algorithm>
4 #include <cstring>
5 #include <cmath>
6 #include <vector>
7 using namespace std;
8 const int maxn = 1e5+5;
9 int t1[maxn],t2[maxn],c[maxn];
10 bool cmp(int *r,int a,int b,int l)
11 {
12 return r[a] == r[b] && r[a+l] == r[b+l];
13 }
14 void da(int str[],int sa[],int Rank[],int height[],int n,int m)
15 {
16 n++;
17 int i, j, p, *x = t1, *y = t2;
18 //第一轮基数排序,如果s的最大值很大,可改为快速排序
19 for(i = 0;i < m;i++)c[i] = 0;
20 for(i = 0;i < n;i++)c[x[i] = str[i]]++;
21 for(i = 1;i < m;i++)c[i] += c[i-1];
22 for(i = n-1;i >= 0;i--)sa[--c[x[i]]] = i;
23 for(j = 1;j <= n; j <<= 1)
24 {
25 p = 0;
26 //直接利用sa数组排序第二关键字
27 for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小
28 for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j;
29 //这样数组y保存的就是按照第二关键字排序的结果
30 //基数排序第一关键字
31 for(i = 0; i < m; i++)c[i] = 0;
32 for(i = 0; i < n; i++)c[x[y[i]]]++;
33 for(i = 1; i < m;i++)c[i] += c[i-1];
34 for(i = n-1; i >= 0;i--)sa[--c[x[y[i]]]] = y[i];
35 //根据sa和x数组计算新的x数组
36 swap(x,y);
37 p = 1; x[sa[0]] = 0;
38 for(i = 1;i < n;i++)
39 x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
40 if(p >= n)break;
41 m = p;//下次基数排序的最大值
42 }
43 int k = 0;
44 n--;
45 for(i = 0;i <= n;i++)Rank[sa[i]] = i;
46 for(i = 0;i < n;i++)
47 {
48 if(k)k--;
49 j = sa[Rank[i]-1];
50 while(str[i+k] == str[j+k])k++;
51 height[Rank[i]] = k;
52 }
53 }
54 int Rank[maxn],height[maxn];
55 int sa[maxn];
56 char s[maxn];
57 int M[maxn][20];
58 int r[maxn];
59 void RMQ(int n)
60 {
61 for(int i=1;i<=n;i++) M[i][0] = height[i];
62 for(int j=1;(1<<j)<=n;j++)
63 {
64 for(int i=1;(i+(1<<j)-1)<=n;i++)
65 {
66 M[i][j] = min(M[i][j-1],M[i+(1<<(j-1))][j-1]);
67 }
68 }
69 }
70 int RMQ_query(int l,int r)
71 {
72 int k = 0;
73 while((1<<(k+1))<=(r-l+1)) k++;
74 return min(M[l][k],M[r-(1<<k)+1][k]);
75 }
76 int lcp(int a,int b)
77 {
78 if(a<0||b<0) return 0;
79 a = Rank[a],b = Rank[b];
80 if(a>b) swap(a,b);
81 return RMQ_query(a+1,b);
82 }
83 vector<int> length;
84 int main()
85 {
86 int kase = 0;
87 while(scanf("%s",s)&&s[0]!=\'#\')
88 {
89 int n = strlen(s);
90 for(int i=0;i<n;i++) r[i] = s[i];
91 r[n] = 0;
92 da(r,sa,Rank,height,n,128);
93 RMQ(n);
94 int maxv = 0;
95 length.clear();
96 printf("Case %d: ",++kase);
97 for(int l=1;l<n;l++)
98 {
99 for(int i=0;i+l<n;i+=l)
100 {
101 int m = lcp(i,i+l);
102 int step = m/l+1; //这必须先除,以免后面移动位置后,M变小了
103 int start = i-(l-m%l); //计算挪到的位置
104 if(start>=0&&m%l>0)
105 {
106 if(lcp(start,start+l)>=m)
107 {
108 step++;
109 }
110 }
111 if(step>maxv)
112 {
113 maxv = step;
114 length.clear();
115 length.push_back(l);
116 }
117 else if(step==maxv)
118 {
119 length.push_back(l);
120 }
121 }
122 }
123 // printf("%d\\n",maxv);
124 int start = 0;
125 int printlen = 0;
126 int flag = 0;
127 //枚举排名,保证字典序最小
128 for(int i=1;i<=n&&!flag;i++)
129 {
130 int tt = length.size();
131 for(int j=0;j<tt;j++)
132 {
133 int l = length[j];
134 if(lcp(sa[i],sa[i]+l)>=(maxv-1)*l)
135 {
136 start = sa[i];
137 printlen = l;
138 flag = 1;
139 break;
140 }
141 }
142 }
143 // printf("%d %d %d\\n",start,maxv,printlen);
144 for(int i=start;i<start+maxv*printlen;i++)
145 {
146 printf("%c",s[i]);
147 }
148 printf("\\n");
149 }
150 return 0;
151 }
以上是关于POJ - 3693 Maximum repetition substring 后缀数组+RMQ的主要内容,如果未能解决你的问题,请参考以下文章
POJ3693Maximum repetition substring (后缀数组+RMQ)(占位)
POJ3693 Maximum repetition substring
POJ 3693 Maximum repetition substring(最多重复次数的子串)
POJ 3693 Maximum repetition substring(后缀数组[重复次数最多的连续重复子串])