KMP

  闲下来的时候干点喜欢做的事情。VIJOS前几天又开放了,某牛说得好,“当年的P少年,现在已变成了JAVA大叔”,不过惭愧的是我对JAVA完全不懂。五年过去了,发生了很多很多的事情,惊喜的是自己的号竟然还被保存着,当年在没有老师教的情况下,自己学习,或是和同学讨论,一个月刷了40题,虽然结果是遗憾的,但是这一个月却对我产生了很大的影响,发现了另一个不同的世界。
  因为自身水平实在有限,所以总会有许多感伤。不过伤感了一会还是回归正题吧,今天打算把KMP写一下。
  很惭愧一直没有学习经典的KMP算法,最近看了点网上的资料,用ZJU的模板AC了poj3461的KMP模板题,有了一点感觉。
  经典的KMP算法基本上分为两个主要的步骤,第一步是预处理(匹配串自匹配),第二步是匹配。第一步是挖掘匹配串中的信息,得到next函数;第二步是利用next函数进行匹配。next函数记录了当发生了失配的情况时,直接跳至某一位点进行继续匹配判断。
  先简单阐述第二步匹配时会出现的失配情况。
  于主串和匹配串的某位点(i_0和j_0)开始,逐一进行对比,当主串中的i和匹配串中的j失配时,即T[i]!=W[j](假设主串为T,匹配串为W)时,如果将i和j跳转至i_0+1和j_0+1重新开始匹配就会浪费大量的信息以减少时间。可以看到,在重新进行匹配时,主串失配处的T[i]会出现两种情况:进行新的匹配,此时需要保证T[i]前的k个字符与W的前k个字符需完全配对,即T[i-k]->T[i-1]与W[0]->W[k-1]一一配对,然后再检查T[i]与W[k]是否配对(利用next函数可以使得失配处j准确定位至k,满足前k个字符一一配对的条件,直接进行T[i]与W[k]的配对,又因next函数具有降序性使得所有情况均能被考虑),在这种情况下,T[i]未能跳过配对区域(需要再次判断T[i]的匹配性);另一种情况则是T[i]跳过配对区域(因为不存在k,使得T[i]前的k个字符与W的前k个字符完全配对),i与j直接跳至i+1与j+1进行重新匹配(T[i]不需要再次判断)。
  下面具体展开KMP算法的操作。
  得到next函数的操作(对于一个长度为m的匹配串W,m为正整数):
  1)next[0]=j=-1,需要j是因为在后续操作过程中可能需要多次next的迭代;
  2)对i操作,从1->m,此时0->i-1已有了next函数(j停留在上一步i-1时的情况),此时j有两种情况,j=-1或j>=0,通过判断W[i]==W[j+1]来得到i时的next函数:若W[i]==W[j+1],则next[i]=j+1;否则,通过迭代j=next[j],使得W[i]==W[j+1],next[i]=j+1;若不可能使得W[i]==W[j+1],则next[j]=-1。
  得到next函数后,匹配的操作:
  用指针i指主串T,用指针j指匹配串W,逐一匹配,判断T[i]==W[j],失配后j=next[j-1]+1,匹对成功则继续匹配,匹对失败(当j=0时,T[i]!=W[j])则i++,j=0。
  next函数的代码:

next[0] = -1;
for(int i = 1, j = -1; i < m; i++)
{
	while(j >= 0 && W[i] != W[j + 1])
		j = next[j];
	if(W[i] == W[j + 1])
		j++;
	next[i] = j;
}

  匹配代码:

for(int i = 0, j = 0; i < len_T; i++)
{
	if(T[i] == W[j])
	{
		j++;
		if(j == len_W)
			succeed;
	}
	else if(j)
	{
		j = next[j - 1] + 1;
		i--;
	}
}

  算法的正确性以后再补充。

posted @ 2012-07-12 11:31  zxfx100  阅读(234)  评论(0编辑  收藏  举报