POJ 3693 Maximum repetition substring

题目大意:

一个字符串的重复数x的含义是, 它可以由至多x个相同的字符串连接而成. 现在给出一个长度为N的字符串, N不超过100000, 求出它的重复数最大且字典序最小的子串.

 

简要分析:

论文题, 思路很有意思. 我们定义重复的字符串为循环节, 那么我们就枚举循环节长度L, 那么如果答案存在, 则必定包含s[0], s[L], s[2 * L]等中相邻的连个. 我们再枚举j, 则相当于我们假定s[j * L]和s[(j + 1) * L]在答案中, 且s[j * L]在第一个循环节内. 这里我们需要求后缀j * L和后缀(j + 1) * L的最长公共前缀, 这个可以用后缀数组和ST算法做好预处理后O(1)回答. 设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j + 1) * L - (L - M % L)的最长公共前缀, 看是否不小于L. 对于字典序的保证, 我的算法没有保证复杂度, 因为我是从每个j * L位置暴力往前for答案的初始位置, 答案的比较用后缀数组就好, 中间加上各种剪枝跑得还凑合. 在忽略那个暴力的情况下, 算法复杂度为O(N * (1 / 1 + 1 / 2 + ... + 1 / N)) = O(NlogN).

 

代码实现:

View Code
  1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAX_N = 100000;
8 char s[MAX_N + 2];
9 int cas, n, sa[MAX_N + 1], rank[MAX_N + 1], cnt[MAX_N + 1], height[MAX_N + 1];
10 int dp[17][MAX_N + 1], log2[MAX_N + 1];
11
12 struct node_t {
13 int v[2], p;
14 bool operator == (const node_t &t) const {
15 return v[0] == t.v[0] && v[1] == t.v[1];
16 }
17 } nd[MAX_N + 1], tp[MAX_N + 1];
18
19 struct ans_t {
20 int rep, start, len;
21 bool operator < (const ans_t &t) const {
22 if (rep != t.rep) return rep < t.rep;
23 if (rank[start] != rank[t.start]) return rank[start] > rank[t.start];
24 return len > t.len;
25 }
26 } ans, tmp;
27
28 void ra(int b) {
29 for (int i = 1; i >= 0; i --) {
30 memset(cnt, 0, sizeof(int) * (b + 1));
31 for (int j = 1; j <= n; j ++) cnt[nd[j].v[i]] ++;
32 for (int j = 1; j <= b; j ++) cnt[j] += cnt[j - 1];
33 for (int j = n; j >= 1; j --) tp[cnt[nd[j].v[i]] --] = nd[j];
34 memcpy(nd, tp, sizeof(node_t) * (n + 1));
35 }
36 for (int i = 1, j = 1, k = 1; i <= n; i = j, k ++)
37 while (j <= n && nd[j] == nd[i]) rank[nd[j ++].p] = k;
38 }
39
40 void rmq_init() {
41 memset(dp, 0x3f, sizeof(dp));
42 for (int i = 1; i <= n; i ++) dp[0][i] = height[i];
43 for (int i = 1; (1 << i) < n; i ++) {
44 for (int j = 1; j <= n; j ++) {
45 dp[i][j] = dp[i - 1][j];
46 if (j + (1 << (i - 1)) <= n) dp[i][j] = min(dp[i][j], dp[i - 1][j + (1 << (i - 1))]);
47 }
48 }
49 }
50
51 int rmq_ask(int l, int r) {
52 if (l > r) swap(l, r);
53 int st = log2[r - l + 1];
54 return min(dp[st][l], dp[st][r - (1 << st) + 1]);
55 }
56
57 int query(int l, int r) {
58 l = rank[l], r = rank[r];
59 if (l > r) swap(l, r);
60 return rmq_ask(++ l, r);
61 }
62
63 void solve() {
64 n = strlen(s + 1);
65 for (int i = 1; i <= n; i ++) {
66 nd[i].v[0] = s[i];
67 nd[i].v[1] = 0;
68 nd[i].p = i;
69 }
70 ra(255);
71 for (int j = 1; j < n; j <<= 1) {
72 for (int i = 1; i <= n; i ++) {
73 nd[i].v[0] = rank[i];
74 nd[i].v[1] = i + j <= n ? rank[i + j] : 0;
75 nd[i].p = i;
76 }
77 ra(n);
78 }
79 for (int i = 1; i <= n; i ++) sa[rank[i]] = i;
80 for (int i = 1, j, k = 0; i <= n; height[rank[i ++]] = k)
81 for (k ? k -- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k ++);
82 rmq_init();
83
84 ans.rep = 1;
85 ans.start = 1;
86 ans.len = 1;
87 for (int i = 2; i <= n; i ++)
88 if (s[i] < s[ans.start]) ans.start = i;
89
90 for (int i = 1; i < n; i ++)
91 for (int j = 1; j + i <= n; j += i) {
92 int t = query(j, j + i);
93 if (t < i) continue;
94 int k = j - (i - t % i);
95 t = t / i + 1;
96 if (t + 1 < ans.rep) continue;
97 if (t + 1 == ans.rep && (k < 1 || query(k, k + i) < i)) continue;
98
99 int p = (t + 1 == ans.rep ? k : j);
100 for ( ; p != j - i && p >= 1; p --) {
101 t = query(p, p + i);
102 t = t / i + 1;
103 if (t < ans.rep) break;
104 tmp.rep = t;
105 tmp.start = p;
106 tmp.len = i;
107 if (ans < tmp) ans = tmp;
108 }
109 }
110
111 for (int i = 0; i < ans.len * ans.rep; i ++) printf("%c", s[ans.start + i]);
112 printf("\n");
113 }
114
115 int main() {
116 log2[1] = 0;
117 for (int i = 2; i <= MAX_N; i ++) {
118 if ((i & (i - 1)) == 0) log2[i] = log2[i - 1] + 1;
119 else log2[i] = log2[i - 1];
120 }
121 while (scanf("%s", s + 1) && s[1] != '#') {
122 printf("Case %d: ", ++ cas);
123 solve();
124 }
125 return 0;
126 }

你可能感兴趣的:(substring)