修正的KMP算法

<span style="font-family: Arial, Helvetica, sans-serif;">package com.algorithm.string;</span>

public class KMP {
	
	private static void nextval(char[] P,int[] next){
		next[1]=0;
		int i=1,j=next[i];
		while(i<P[0]){                         //这里与教材中不同
			if(j==0||P[i]==P[j]){
				i++;
				j++;
				while(P[i]==P[j]&&j>=1){
					j=next[j];
				}
				next[i]=j;
			}else{
				j=next[j];
			}
		}
	}
	public static int match(char[] T,char[] P,int[] next){
		int i=1,j=1;
		while(i<=T[0]&&j<=P[0]){
			if(j==0||T[i]==P[j]){
				i++;
				j++;
			}else{
				j=next[j];
			}
		}
		if(j>P[0]){
			return i-P[0];
		}else{
			return -1;
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		char[] T=new char[]{10,0,1,2,2,4,2,2,2,8,9};
		char[] P=new char[]{3,2,2,2};
		int[] next=new int[4];
		nextval(P,next);
		System.out.println(match(T,P,next));
	}

}

输出结果:6

    

        先说明一下,本文的基础是严蔚敏的《数据结构》C语言版,字符串数组第0个元素用来存储字符串长度。上面是java实现的KMP修正算法,与教材中不同的是当出现需要修正的情况时,这个程序使用循环结构不停的滑动逻辑模式串,直至P[i]!=P[j],而教材中只滑动了一次。

        KMP算法的关键就在于next数组的构造,这个数组的表意非常绕口,很容易造成逻辑上的混乱,这也是KMP难于记忆的原因。next[i]的值代表的实际意义为:模式串中前i-1个字符组成的子串的最长前后缀字符串之后第一个字符的索引。前后缀字符串是我自己取的名字,它表示同时为前缀和后缀的字符串,比如说字符串cbcb中,cb即为前后缀字符串,但是注意,字符串cbcb本身并不是它自己的前后缀字符串,虽然它的确即为前缀也为后缀,这只是一种规定。上面是next数组的一般含义,但是有两个特例,一是当前i-1个字符组成的子串的前后缀字符串不存在时,next[i]=1,二是规定next[1]=0。所以next[i]的取值一共有三种情况,与数据结构教材中的那个数学公式是一致的,这里就不贴那个公式了。以下图中模式串P为例,构造next数组,next[0]我们闲置它,next[1]——next[6]分别为{0,1,1,3,3,4}。构造出next数组之后的匹配过程是很容易理解的,如下图匹配至i=7,j=5时,T[7]!=P[5],只需令j=next[j],继续与T[7]匹配,至于为什么,是一个等式传递性的问题,数据结构教材中都有提到,也比较好理解。

    
       下面主要说一下next数组的构造过程,其实从next数组的定义我们可以知道对于任意一个字符串来说,next[2]一定是等于1的,因为前2-1=1个字符组成的字符串不存在前后缀字符串。而且我们可以证明,next[i]与next[i-1]之间存在数量关系,因此可以用递推的方法求得next数组。如下图所示,next数组的构造过程我们可以想象成两个字符串的匹配过程,分别是逻辑主串P1,逻辑模式串P2,之所以称之为逻辑字符串,是因为P1和P2物理上是同一个字符串——模式串P。假设当前已求得next[i]=3,现在来求next[i+1]。不难想象,由于模式串中前2个字符组成的字符串与第i个字符前面的2个字符组成的字符串(不包括第i个字符)相等,所以若P[3]=P[i],则模式串中前3个字符组成的字符串必与第i+1个字符前面的3个字符组成的字符串(不包括第i+1个字符)相等,故此时前i个字符组成的字符串的最长前后缀字符串长度为3,故next[i+1]=3+1=4。抽象出来,即:若next[i+1]=j+1;若P[j=next[i]]!=P[i],则设j=next[j],要继续比较P[i]和P[j],直至相等或j=0。如下图next[5]=3,现在来求next[6]。我们来比较P[5]和P[next[5]]=P[3],由于它们相等,故next[6]=4。这是原始的KMP算法,这里有一个可以优化的地方,我们知道,当主串T与模式串P匹配时,若匹配到模式串第6个字符失配时,则要拿模式串中第next[6]=4个字符与主串当前字符比较,当P[6]与主串当前字符不匹配时,若P[4]=P[6],显然P[4]也不匹配,故在计算next[6]时,逻辑模式串还要继续向右滑动next[4]=2个单位,此时得到next[6]=2  ,由于P[6]与P[2]相等,还要将逻辑模式串在向右滑动next[2]=1个单位,故next[6]=1。

    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值