KMP算法的理解

学了一天终于了解KMP算法了,下面我通过图片讲解一下。

 先说一下Bru-Force算法,比如我们要从序列:

1 2 3 1 2 3 1 2 3 1 2 3 4中寻找序列1 2  3 1 2 3 4我们可以进行如下操作:

 

。。。。。。

就这样一步一步可以类推出来子序列在目标串中的位置,但是这种效率如果碰到特殊情况如:目标串为:aaaaaaaaab,目标串:aaab的情况,算法的复杂度就会落到O(n*m),其中n*m分别为目标串和子序列的长度。

而KMP算法可以对这个问题进行优化,下面我们解释KMP算法。

说到KMP算法,首先要说的就是NEXT数组,我们先求出子序列 1 2 3 1 2 3 4的NEXT数组:

我们先不说这个表是怎么来的,我们先看看这东西怎么用,一会再说这个东西怎么求。

先看第一次遍历:

我们发现这个4这个的位置和1不相等,然后我们看一下,4的next数组对应值是3。然后我们就可以直接用子序列下标为3的地方和目标串与子序列不相等那个1比较(注意一点就是如果next数组是-1的话,那子序列从头开始比较,目标串比较位置右移一位),就是打星星的地方:

发现依然是这个4和那个1不相等,方法同上:

然后我们就能求得这个问题的最终结果,这样打打减少了比较的次数。

那么这个目标串什么求呢?

要弄懂这个先要清楚前后缀。

我还以1 2 3 1 2 3 4为例子,

其中1没有前后缀。

1 2:前缀:{1};后缀:{2}。

1 2 3:前缀{1,1  2};后缀{3, 2  3}。

1 2 3 1:前缀{1,1  2,1  2  3};后缀{1,3  1;2  3  1}。

1 2 3 1 2:前缀{1,1  2,1  2  3,1 2 3 1};后缀:{2,1  2,3  1  2,2  3  1  2}

.............

应该很显而易见。

然后假定pattern[i]中最大相等缀值为pattern[0]-pattern[i]中最长的,前后缀相等的长度。

然后next值的公式大概就是:

就以1 2 3 1 2 3 4为例子。

next[0]:     其中pattern[0]=1=pattern[0];所以next[0]=-1。

next[1]:     pattern[1]=2,  next[1]=pattern[0]的相等缀值,而pattern[0]没有相等缀值,也就是0,所以next[1]=0。

next[2]:     参考next[1]

next[3]:     参考next[1]。其中pattern[3]=1=pattern[0];所以next[3]=-1。

next[4]:    先看pattern[4]的序列为1 2 3 1 2具有前后相同缀{1 2},所以next[4]=0。

next[5]:    参考next[4]

next[6]:   我们看4前面的序列为1 2 3 1 2 3。显而易见可得pattern[5]的相等前后缀都是{1,2,3},缀值为3,所以推出next[6]=4。

最后我们就可以的出来子序列1 2 3 1 2 3 4的next数组为:-1  0  0  -1  0  0  3。

下面贴代码。(JAVA)

计算next数组:

	public static int[] getNext(int[]pattern){			//返回值即为next数组
		int next[]=new int[pattern.length];				//储存位置。
		int j=0;										//遍历到的位置
		int k=-1;										//到目前位置相同缀的长度
		next[0]=-1;
		while(j<pattern.length-1){
			if(k==-1||pattern[j]==pattern[k]){			
				j++;
				k++;
				if(pattern[j]==pattern[k])				//如果这个值和前面的匹配到了,那这个next值归0
					next[j]=0;
				else 
					next[j]=k;
			}
			else										//如果相等缀值不为-1并且这个点和第一个点的值不一样,相等缀值为-1
				k=-1;
		}
		return next;
	}

计算insertOf查找结果代码:

 

	public static int indexOf(int target[],int pattern[],int begin){
		int n=target.length,m=pattern.length;
		int i=begin,j=0;								//i储存目标串遍历位置,j储存子序列遍历位置
		int[]next=getNext(pattern);						//获取next数组
		while(j<pattern.length){	
			if(j==-1||target[i]==pattern[j]){			
				i++;									//如果读取到-1或者二者匹配值,那目标串和子序列都移一位
				j++;
			}
			else{										//否则如果匹配不相等,子序列移位next
				j=next[j];
				if(n-i<m-j)								//如果目标序列长度不够,终止循环
					break;
			}
		}
		if(j==m)return i-j-1;
		return -1;	
	}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值