KMP--next数组的理解

时间:2020-12-04 05:59:41

刚开始接触这个,网上都说next数组时KMP的精华,很多题都用到了next数组的性质,折腾了一天终于对next数组的来源、作用有了大致了解,看了网上很多讲解,最终在学长发的ppt的过程,茅塞顿开KMP--next数组的理解,懂了一丢丢。

1.KMP算法的作用

  KMP算法是用来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。例如”Today is Tuesday”.中是否包含”day”,在哪些位置包含

假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,

  A[i-j+ 1..i]与B[1..j]完全相等。(这句话很关键)
也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符

(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。

当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。

从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。
  P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。(这句话很关键,这也就是俗称的next 数组的最初来源)
但有可能j到了0仍然不能满足A[i+1]=B[j+1],当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。
2.next数组的结论:

如果对于next数组中的 i 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , 则说明字符串循环,而且

循环节长度为:   i - next[i]

循环次数为:       i / ( i - next[i] )

(这个是别人总结的结论,数学思维好强大)

代码实现:

void getn()
{
	int i=0;
	int j=-1;
	next[i]=j;
	while(i<l1)
	{
		if(j==-1||w[i]==w[j])
		{
			i++;
			j++;
			next[i]=j;
			{
				if(next[i]==0)
				ans++;
			}
		}
		else j=next[j];
	}
}//ans=0开始,w为字符型数组 

poj--seek the name,seek the fame(这便用到了上面特大字的类容)

#include<stdio.h>
#include<string.h>
#define M 400010
char a[M];
int per[M];
int l;
int z[M];
void getp()
{
	int i,j;
	i=0;j=-1;
	per[i]=j;
	while(i<l)
	{
		if(j==-1||a[i]==a[j])
		{
			i++;j++;
			per[i]=j;
		}
		else j=per[j];
	}
	
}
int main()
{
	int i;
	while(scanf("%s",a)!=EOF)
	{
		l=strlen(a);
		getp();
		z[0]=l;
		int k=1;
		for(i=l;i>=0;)
		{
			if(per[i]<=0)break;
			z[k++]=per[i];
			i=per[i];
		}
		for(i=k-1;i>=1;i--)
		printf("%d ",z[i]);
		printf("%d\n",z[i]);
	}
	return 0;
}