kmp—Period讲解

经过这段时间的学习,对kmp又有了更深的了解,于是就做了一道比较有水平的题,这道题呢其实也并不是一点理解,只是有些东西一时半会的不是那么容易想出来。好了下面来分享这道题吧。
题目描述:

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as A K ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.
Input
The input consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S.The second line contains the string S. The input file ends with a line, having the
number zero on it.
Output
For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.
Sample Input

3
aaa
12
aabaabaabaab
0
1
2
3
4
5
Sample Output

Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4
1
2
3
4
5
6
7
8
9
题意分析:这道题说白了就是让你寻找一个字符串中的存在的循环体以及他的长度及循环次数。例如第二个样例aabaabaabaab,当从第一个字母开始往后长度为2的时候及为aa的时候,最小循环体为a,循环了两次,所以输出了当前的长度2,以及当前长度中最小循环体循环的次数2,当长度为6的时候也即到达aabaab时,此时这个串中最小循环体是aab在这个串中循环两次,因此输出6,2,当长度为9的时候aabaabaab时最小循环体为aab,在这之中循环了三次,因此输出9,3。下面的12 ,4同理。

解题思路:看了我的题解,相信你一定能读懂这道题是干嘛的了,可能不知道怎么去实现,好了现在先告诉你方法,最后再给你代码,先告诉你怎么求最小循环体的长度,我假设用k来记录当前走到的长度,当然需要从2开始(一个字母不存在最小循环体,也谈不上计算循环的次数,因此k的初始值为2),用t来表示最小循环体的长度,t与k之间就存在着这样的关系t=k-next[k];你可能会问为什么再好的解释就是自己代入样例去算一下,这样对对你模拟代码运行的过程也是有所帮住的。

#include<stdio.h>
#include<string.h>
char a[1001000];
int next[1010000],n;
//这都是kmp中next 数组的一个模板
void get_next()
{
	int i=1,j=0;
	next[0]=-1;
	while(i<n)
	{
		if(j==-1||a[i]==a[j])
		{
			i++;
			j++;
			next[i]=j;
		}
		else
			j=next[j];
	}
}
int main()
{
	int k,t,s=0,i;
	while(scanf("%d",&n),n!=0)
	{
		getchar();
		s++;
		for(i=0;i<n;i++)
			scanf("%c",&a[i]);
		get_next();//将字符串中前后缀相等的字母都记录下对应的次数
		printf("Test case #%d\n",r);
		for(k=2;k<=n;k++)
		{
			/*next[K]表示的是第K个字母前(字符串从零开始,第k个字母
			实际相当于第k+1个字母)的那个字符串中前缀和后缀相等字符的个数*/
			t=k-next[k];
			/*t不等不k是因为假设t与k相等就表示next[k]等于零,而
			我刚才说了nex[k]表示的是第k个字母以前(注意此时的k,字符串数组从零开始)
			的字符串中前缀和后缀字母相等个数,如果等于零就代表第k个字母前的字符串前
			和后缀没有相等的字符,那就不用谈最小循环体了,因此t不等于k,至于k对t
			求余为啥等于零就不用我多说了吧,想想就知道为啥*/
			if(t!=k&&k%t==0)
				printf("%d %d\n",k,k/t);
		}
		printf("\n");
	}
	return 0;
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮皮皮皮皮皮卡乒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值