SP263 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
", 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++

原文地址:https://www.cnblogs.com/xcxc82/p/13938028.html