题目大意:
就是给出一个串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;
}