题目大意:
就是给出一个串S 长度不超过10^6, 求最大周期使得S = a^n也就是S是有n个字符串a连接起来的,求最大的n(也就是找到最短的a即可)
大致思路:
首先利用KMP的next数组可以知道循环节的个数, 为n/(n - next[n]) n是S的长度, 这个感觉还是有点晕...
另外一个做法是使用后缀数组
KMP的做法:
代码如下:
Result : Accepted Memory : 5024 KB Time : 141 ms
/* * Author: Gatevin * Created Time: 2015/2/2 15:07:55 * File Name: Iris_Freyja.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; /* * 首先如果S = a^n由next数组定义next[n]的值满足S[0~next[n] - 1] == S[(n - next[n]) ~ (n - 1)] * 如果设S1 = a^(n - 1)则S = S1*a那么在n处失配时由于由next数组的定义转到S1的结尾位置后一位进行匹配 * S[0~next[n] - 1] == S[(n - next[n]) ~ (n - 1)] = S1 * 那么n - next[n]刚好是循环节的长度, 且n % (n - next[n]) == 0 * 有种证明说不清楚的感觉....但是当n % (n - next[n]) == 0 的时候的确很好证明循环节是n - next[n] * 可是n % (n - next[n]) != 0的时候为什么没有循环节这个还没证明出来... */ char s[1000010]; int next[1000010]; int main() { while(scanf("%s", s)) { if(strlen(s) == 1 && s[0] == '.') break; memset(next, 0, sizeof(next)); int n = strlen(s); for(int i = 1; i < n; i++) { int j = i; while(j > 0) { j = next[j]; if(s[i] == s[j]) { next[i + 1] = j + 1; break; } } } if(n % (n - next[n]) == 0) printf("%d\n", n/(n - next[n])); else printf("1\n"); } return 0; }
复杂虽然是O(n)但是常数看来很大= =
思路见代码注释, 另外求后缀数组的时候用倍增算法超时了..要用DC3算法
Result : Accepted Memory : 45508 KB Time : 2750 ms
/* * Author: Gatevin * Created Time: 2015/2/2 19:21:53 * File Name: Iris_Freyja.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 1000233 #define F(x) ((x)/3 + ((x) % 3 == 1 ? 0 : tb)) #define G(x) ((x) < tb ? (x)*3 + 1 : ((x) - tb)*3 + 2) /* * DC3算法求后缀数组...倍增算法会超时.. */ int wa[maxn], wb[maxn], wv[maxn], Ws[maxn]; int c0(int *r, int a, int b) { return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2]; } int c12(int k, int *r, int a, int b) { if(k == 2) return r[a] < r[b] || (r[a] == r[b] && c12(1, r, a + 1, b + 1)); else return r[a] < r[b] || (r[a] == r[b] && wv[a + 1] < wv[b + 1]); } void sort(int* r, int *a, int *b, int n, int m) { int i; for(i = 0; i < n; i++) wv[i] = r[a[i]]; for(i = 0; i < m; i++) Ws[i] = 0; for(i = 0; i < n; i++) Ws[wv[i]]++; for(i = 1; i < m; i++) Ws[i] += Ws[i - 1]; for(i = n - 1; i >= 0; i--) b[--Ws[wv[i]]] = a[i]; return; } void dc3(int *r, int *sa, int n, int m) { int i, j, *rn = r + n, *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p; r[n] = r[n + 1] = 0; for(i = 0; i < n; i++) if(i % 3 != 0) wa[tbc++] = i; sort(r + 2, wa, wb, tbc, m); sort(r + 1, wb, wa, tbc, m); sort(r, wa, wb, tbc, m); for(p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++) rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++; if(p < tbc) dc3(rn, san, tbc, p); else for(i = 0; i < tbc; i++) san[rn[i]] = i; for(i = 0; i < tbc; i++) if(san[i] < tb) wb[ta++] = san[i] * 3; if(n % 3 == 1) wb[ta++] = n - 1; sort(r, wb, wa, ta, m); for(i = 0; i < tbc; i++) wv[wb[i] = G(san[i])] = i; for(i = 0, j = 0, p = 0; i < ta && j < tbc; p++) sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++]; for(; i < ta; p++) sa[p] = wa[i++]; for(; j < tbc; p++) sa[p] = wb[j++]; return; } int rank[maxn], height[maxn]; void calheight(int* r, int* sa, int n) { int i, j, k = 0; for(i = 1; i <= n; i++) rank[sa[i]] = i; for(i = 0; i < n; height[rank[i++]] = k) for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++); return; } int s[3*maxn]; char ts[maxn]; int sa[3*maxn]; int lcp[maxn]; /* * 如果我们暴力枚举S的前k个字符会是一个循环节, * 那么Suffix(0)(从s[0]开始的后缀)与Suffix(k)会具有LCP值是n - k的关系 * 如果n % k == 0且LCP(rank[0], rank[k]) == n - k, 则前k个字符是S的循环节(可证明) * 而由LCP Theorem可知对于i < j 有 LCP(i, j) = min{LCP(k - 1, k) | i + 1 <= k < j} * 因此如果用lcp[i]表示LCP(rank[0], i)的话 * 利用height数组有height[i] = LCP(i, i - 1) * 则对于i < rank[0] lcp[i] = min(height[i + 1], lcp[i + 1]) * 对于i > rank[0] lcp[i] = min(height[i], lcp[i - 1]) * 递推求出lcp数组即可(这里因为rank[0]固定所以没有必要使用RMQ的查询问题, 预处理更简单) * 这样枚举长度k从1到n/2, 如果存在lcp[ran[k]] = n - k 即LCP(rank[0], rank[k])的话, k是满足条件的循环节 * 找出最小的这样的k即可 */ int main() { while(scanf("%s", ts)) { int n = strlen(ts); if(n == 1 && ts[0] == '.') break; for(int i = 0; i < n; i++) s[i] = ts[i]; s[n] = 0; dc3(s, sa, n + 1, 260);//可打印字符单位不会超过ASCII码表= = calheight(s, sa, n); //递推求解lcp[i] = LCP(rank[0], i) int t = rank[0]; lcp[t - 1] = height[t]; lcp[t + 1] = height[t + 1]; for(int i = t - 2; i >= 0; i--) lcp[i] = min(height[i + 1], lcp[i + 1]); for(int i = t + 2; i <= n; i++) lcp[i] = min(height[i], lcp[i - 1]); for(int k = 1; k <= (n >> 1); k++) { if(n % k == 0 && lcp[rank[k]] == n - k)//寻找满足条件的最小的k { printf("%d\n", n/k); goto nex; } } printf("1\n"); nex:; } return 0; }