SP263 PERIOD - Period

题目描述

如果一个字符串\(S\)是由一个字符串\(T\)重复\(K\)次形成的,则称\(T\)\(S\)的循环元。使\(K\)最大的字符串\(T\)称为\(S\)的最小循环元,此时的\(K\)称为最大循环次数。

现给一个给定长度为N的字符串\(S\),对\(S\)的每一个前缀\(S[1\)~\(i]\),如果它的最大循环次数大于\(1\),则输出该前缀的长度和最大循环次数。

分析

引理

\(S[1\)-\(i]\)具有长度\(len<i\)的循环元的充要条件是\(len\)能整除\(i\)并且\(S[len+1\)-\(i]=S[1\)~\(i-len]\)(即\(i-len\)\(KMP\)算法中\(next[i]\)的"候选择")

证明:

首先,\(len\)能作为\(S\)的循环元,必须满足\(len\)能整除\(i\),且\(S[len+1\)-\(i]\)\(S[1\)-\(i-len]\)都是由\(i/len-1\)个循环元构成的,即\(S[len+1\)-\(i]=S[1\)-\(i-len]\)

其次,分别从\(S[len+1\)-\(i]\)\(S[1\)-\(i-len]\)取前\(len\)个字符,可以发现\(S[len+1\)-\(2*len]\) \(=\) \(S[1\)-\(len]\),依此类推,可以发现\(S[len+1\)-\(i]\)\(S[1\)-\(i-len]\)是以\(len\)为间隔错位对齐的,故\(S[1\)-\(len]\)\(S\)的循环元

证毕

\(code\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000010;
int n;
int next[MAXN];
char s[MAXN],s2[MAXN];
void pre(){
	int j = 0;
	for(int i=2;i<=n;i++){
		while(j>0&&s[i]!=s[j+1]) j = next[j];
		if(s[i]==s[j+1]) j++;
		next[i] = j;
	}
}
int main(){
	int t;
	cin>>t;
	int now = 0;
	while(t--){
		now++;
		cin>>n;
		for(int i=1;i<=n;i++) cin>>s[i];
		pre();	
		printf("Test case #%d\n", now);
		for(int i=1;i<=n;i++)
			if(i%(i-next[i])==0&&i/(i-next[i])>1){
				cout<<i<<" "<<i/(i-next[i])<<endl;
			}
		}
} 

参考资料

《算法竞赛进阶指南》-李煜东

CSP2020 RP++

posted @ 2020-11-06 17:29  xcxc82  阅读(71)  评论(0编辑  收藏  举报