POJ2406——KMP

玄学的KMP算法……

点击看题

题意是给一个字符串,求最小循环节的循环次数

按kmp的一般思路,我们先来求一个p数组(一般叫next数组),p[i]表示模式串第i位如果和文本串当中某一位不匹配、则需退回p[i]位重新和这一位匹配。

明显地,对于模式串来讲,第1位到第p[n]位和第n-p[n]位到第n位是匹配的。如果n%(n-p[n])=0,那么重复连续子串存在且长度为n-p[n](我会说我根本不懂怎么证明么

但是还是有大神懂怎么证明的,以下转自Euler_M的博客(http://blog.csdn.net/euler_m/article/details/6281903):

======================================================================

 假设有这么一个字符串

如果s[a..b]==s[c...d],也就是说,b..c没有被算进去,就是a..c没有前缀和后缀是一样的,很显然next[d]的值肯定是小于l/2的。

l%(l-next[l])!=0.这样的字符串是不存在最小重复子串的就像前面说的abcccab就是这种类型

如果存在最小重复子串那么,next[d]就可以由next[a..b]也就是可以由前面的next[]来构成

我们在构造next时候就是这么构造的,

if(B[j+1]==B[i]) 

         j++;

这里l-next[l]得到的就是a...b,因为后面的next都可以由前面的得到,那么b..d肯定也是由a..b组成的,a..b就是它的最小重复子串。

重复次数就是l/(l-next[l])

=================================================================================

好吧,代码如下(其后会纳入模版中):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define repu(i,r,l) for(int i=r;i>=l;i--)
using namespace std;

int n,m,lena,j,p[1001000];
char a[1001000],ch;
int solve()
{
	if (lena==1) return 1;
	memset(p,0,sizeof(p));
	j=0;
	p[1]=0;
	rep(i,2,lena)
	{
		while((a[j+1]!=a[i])&&(j)) j=p[j];
		if (a[j+1]==a[i]) j++;
		p[i]=j;
	}
	if (p[lena]==0) return 1;
    return lena%(lena-p[lena])?1:int(lena/(lena-p[lena]));
}
int main()
{
	lena=0;
	ch=getchar();
	while((ch>='a')&&(ch<='z'))
	{
		lena++;
		a[lena]=ch;
		ch=getchar();
	}
	while(lena||ch!='.')
	{
		printf("%d\n",solve());
		lena=0;
	    ch=getchar();
	    while((ch>='a')&&(ch<='z'))
	    {
		    lena++;
	    	a[lena]=ch;
	    	ch=getchar();
	    }
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值