POJ 3693 (后缀数组) Maximum repetition substring
找重复次数最多的字串,如果有多解,要求字典序最小。
我也是跟着罗穗骞菊苣的论文才刷这道题的。
首先还是枚举一个循环节的长度L,如果它出现两次的话,一定会包含s[0], s[L], s[2L]这些相邻两个之间。
然后枚举相邻的两个,尽可能的向前和向后延伸,假设延伸长度为k,则重复次数为k / L + 1
向后延伸很自然的就是求一次LCP,这个用RMQ预处理一下就可以O(1)查询。
向前延伸就是考虑到,我们枚举的s[i*L]和s[(i+1)*L]并不一定是字串的开头,所以向前移动i - (k % i)个位置。
为什么向前移动这么多,就是因为这样的话,LCP的长度就正好是i的整数倍了。
所以向前移动以后,再求一次LCP,看看能否够i - (k % i)这么多,够的话这个串的重复次数再加1.
至于要字典序最小,就得用height数组天生自带字典序。把所有重复次数最多的长度记录下来,然后按字典序枚举,只要找到一组就输出。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 100000 + 10; 7 char s[maxn]; 8 int n; 9 int sa[maxn], rank[maxn], height[maxn]; 10 int t[maxn], t2[maxn], c[256]; 11 12 void build_sa(int n, int m) 13 { 14 int i, *x = t, *y = t2; 15 for(i = 0; i < m; i++) c[i] = 0; 16 for(i = 0; i < n; i++) c[x[i] = s[i]]++; 17 for(i = 1; i < m; i++) c[i] += c[i - 1]; 18 for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; 19 for(int k = 1; k <= n; k <<= 1) 20 { 21 int p = 0; 22 for(i = n - k; i < n; i++) y[p++] = i; 23 for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k; 24 for(i = 0; i < m; i++) c[i] = 0; 25 for(i = 0; i < n; i++) c[x[y[i]]]++; 26 for(i = 1; i < m; i++) c[i] += c[i - 1]; 27 for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; 28 swap(x, y); 29 p = 1; x[sa[0]] = 0; 30 for(i = 1; i < n; i++) 31 x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p - 1 : p++; 32 if(p >= n) break; 33 m = p; 34 } 35 } 36 37 void build_height() 38 { 39 int k = 0; 40 for(int i = 1; i <= n; i++) rank[sa[i]] = i; 41 for(int i = 0; i < n; i++) 42 { 43 if(k) k--; 44 int j = sa[rank[i] - 1]; 45 while(s[i + k] == s[j + k]) k++; 46 height[rank[i]] = k; 47 } 48 } 49 50 int d[maxn][20]; 51 52 void init_RMQ() 53 { 54 for(int i = 0; i < n; i++) d[i][0] = height[i + 1]; 55 for(int j = 1; (1 << j) <= n; j++) 56 for(int i = 0; i + (1 << j) - 1 < n; i++) 57 d[i][j] = min(d[i][j-1], d[i + (1<<(j-1))][j-1]); 58 } 59 60 int RMQ(int L, int R) 61 { 62 int k = 0; 63 while( (1 << (k+1)) <= (R - L + 1) ) k++; 64 return min(d[L][k], d[R-(1<<k)+1][k]); 65 } 66 67 int LCP(int i, int j) 68 { 69 i = rank[i] - 1; j = rank[j] - 1; 70 if(i > j) swap(i, j); 71 return RMQ(i + 1, j); 72 } 73 74 int a[maxn], cnt, maxl; 75 76 int main() 77 { 78 //freopen("in.txt", "r", stdin); 79 80 int kase = 0; 81 while(scanf("%s", s) == 1 && s[0] != '#') 82 { 83 n = strlen(s); 84 build_sa(n + 1, 256); 85 build_height(); 86 init_RMQ(); 87 88 cnt = maxl = 0; 89 for(int i = 1; i < n; i++) 90 { 91 for(int j = 0; j + i < n; j += i) 92 { 93 int k = LCP(j, j + i); 94 int t = k / i + 1; 95 int left = i - (k % i); 96 int head = j - left; 97 if(head >= 0 && LCP(head, head + i) >= left) t++; 98 if(t > maxl) 99 { 100 cnt = 0; 101 a[cnt++] = i; 102 maxl = t; 103 } 104 if(t == maxl) a[cnt++] = i; 105 } 106 } 107 108 int len = -1, st; 109 for(int i = 1; i <= n && len == -1; i++) 110 { 111 for(int j = 0; j < cnt; j++) 112 { 113 int l = a[j]; 114 if(LCP(sa[i], sa[i] + l) >= (maxl - 1) * l) 115 { 116 len = l; 117 st = sa[i]; 118 break; 119 } 120 } 121 } 122 123 printf("Case %d: ", ++kase); 124 for(int i = 0; i < len * maxl; i++) printf("%c", s[st + i]); 125 puts(""); 126 } 127 128 return 0; 129 }