【字符串循环节】 Period
传送门
题意
给定一个长度为\(N\)的字符串\(S\),判断该字符串的所有前缀是否存在循环节
即对于每一个从头开始的长度为 \(i(i>1)\)的前缀,是否由重复出现的子串\(A\)组成,即 \(AAA\dots A\) (\(A\)重复出现\(K\)次,\(K>1\))
若存在,请找出最小的循环节循环的次数\(K\)
数据范围
\(2 \leq N \leq 1000000\)
题解
引理:
-
前缀\(S[1\sim i]\)具有长度为\(len < i\)的循环节的充要条件是\(len\)能整除\(i\),且\(S[i-next[i]+1\sim i]=S[1\sim i]\)
-
\(i-next[i]\)能整除\(i\)时,\(S[1\sim i-next[i]]\)就是\(S[1\sim i]\)的最小循环节,循环次数为\(\frac{i}{(i-next[i])}\)
求出整个串的\(next\)数组,对于每个前缀\(S[1\sim i]\),\(i-next[i]\)就是前缀的循环节的最小长度,
如果\(i\)能整除,则前缀\(i\)由循环节完整循环构成,求的使重复次数\(>1\)的循环节,还要判断是否\(>1\)
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define ll long long
const int N=1e6+10;
int ne[N];
int n;
char s[N];
int _;
int id;
void solve()
{
memset(ne,0,sizeof ne);
cin>>s+1;
int j=0;
rep(i,2,n+1)
{
while(j && s[i]!=s[j+1]) j=ne[j];
if(s[i]==s[j+1]) j++;
ne[i] = j;
}
cout<<"Test case #"<<++id<<endl;
rep(i,2,n+1)
if(i%(i-ne[i])==0 && i/(i-ne[i])>1)
cout<<i<<' '<<i/(i-ne[i])<<endl;
cout<<endl;
}
int main()
{
while(cin>>n && n) solve();
}

浙公网安备 33010602011771号