《算法竞赛进阶指南》周期

周期

一个字符串的前缀是从第一个字符开始的连续若干个字符,例如”abaab”共有5个前缀,分别是a, ab, aba, abaa, abaab。

我们希望知道一个N位字符串S的前缀是否具有循环节。

换言之,对于每一个从头开始的长度为 i (i>1)的前缀,是否由重复出现的子串A组成,即 AAA…A (A重复出现K次,K>1)。

如果存在,请找出最短的循环节对应的K值(也就是这个前缀串的所有可能重复节中,最大的K值)。

输入格式
输入包括多组测试数据,每组测试数据包括两行。

第一行输入字符串S的长度N。

第二行输入字符串S。

输入数据以只包括一个0的行作为结尾。

输出格式
对于每组测试数据,第一行输出 “Test case #” 和测试数据的编号。

接下来的每一行,输出具有循环节的前缀的长度i和其对应K,中间用一个空格隔开。

前缀长度需要升序排列。

在每组测试数据的最后输出一个空行。

数据范围
2≤N≤1000000
输入样例:
3
aaa
4
abcd
12
aabaabaabaab
0
输出样例:
Test case #1
2 2
3 3

Test case #2

Test case #3
2 2
6 2
9 3
12 4

本题是一个典型的KMP字符串匹配算法,关于它本身和重点的next数组在字符串中已经解释了。

另外本题还利用到了KMP算法的一个性质

《算法竞赛进阶指南》周期_第1张图片

#include 
using namespace std;
const int N=1000010;
int nxt[N];
char str[N];
int n;
void get_next()
{
	//KMP算法核心就是计算next(这里指nxt)数组
    //nxt[i](i>=2)表示的是1~i中存在一个最大的K
    //使得1~k-1的每一个元素和i-k+1~i的每一个元素意义对应
	for(int i=2,j=0;i<=n;i++)
	{
		while(j&&str[i]!=str[j+1])j=nxt[j];
		//首先我们知道在该循环之前j指向的是上一个循环即i=i-1时
		//的那个最大的k值,再次基础上我们判断i=i时的字符和j=j+1时的字符是否相等
		//当不相等时,我们要在j=nxt[j],nxt[j]上继续去找
		//我们由上面定义可以知道因为有k值的最大性和要保证前缀、后缀的一对一性
		//即首先要保证从 j、i-1向前的一一对应,因为此时str[i]!=str[j+1]
		//所以我们还要保持上一行的叙述的话,根据定义来说就要有j=nxt[j]
		//继续遍历直到找到一个前缀使得到达当前的后缀一一对应的话结束
		//或者j恢复了起始表示没有一个前缀和
		if(str[i]==str[j+1])j++;
		//由上一语句可知当我们遍历到满足当前情况时,j的位置要向前移动
		nxt[i]=j;
		//最终j的位置就表示KMP算法中的next[i]的值
	}
}
int main()
{
	ios::sync_with_stdio(false);
	int T=1;
	while(cin>>n,n)
	{
		cin>>(str+1);
		get_next();//KMP算法核心
		cout<<"Test case #"<<T++<<endl;
		for(int i=2;i<=n;i++)
		{//从第二个位置开始可能出现前缀与后缀相同的情况
			int t=i- nxt[i];
			//根据性质可得到循环节的大小
			if(i>t&&i%t==0)cout<<i<<' '<<i/t<<endl;
		}
		cout<<endl;
	}
	return 0;
}

你可能感兴趣的:(《算法竞赛进阶指南》,KMP)