快速理解KMP算法——串的模式匹配改进算法

快速理解KMP算法——串的模式匹配改进算法

 
关于数据结构中对串的操作,除了一些基本操作如增删、复制和比较之外,还有一种就是串的模式匹配。翻译下 就是定位已知子串在主串中的位置。
下面我们用一个例子来演示。
 

一个简单例子

主串:a c a b a b c a
子串:a b c

我们要做的是在主串中查找子串的位置。

  • 下面用A表示主串,B表示子串
  • 指针 i :主串中当前比较的元素位置
  • 指针 j :子串中当前比较的元素位置

原始暴力算法

我们先用一种简单的方法实现这个功能。
 
我们从开头开始比较

  • 第一次匹配
    Aa c a b a b c a
    Ba b c
    开始时的位置:i1
    停止时的位置:i2 j2

  • 第二次匹配
    A:a c a b a b c a
    B:   a b c
    开始时的位置:i2
    停止时的位置:i2 j1

  • 第三次匹配
    A:a c a b a b c a
    B:     a b c
    开始时的位置:i3
    停止时的位置:i5 j3

  • 第四次匹配
    A:a c a b a b c a
    B:        a b c
    开始时的位置:i4
    停止时的位置:i4 j1

  • 第五次匹配
    A:a c a b a b c a
    B:           a b c
    开始时的位置:i5
    停止时的位置:i8 j4(结束)

此种方法的时间复杂度在最坏的情况下达到了(主串长度*子串长度)!!!
这是因为 i 还需要回退

Created with Raphaël 2.2.0 开始 i = 1, j = 1 A[i] == B[j]? 全部匹配成功了吗? 结束 i++,j++ i = i - j + 2,j = 1 yes no yes no

KMP改进算法

KMP算法的改进之处是它对子串做了一定的处理,使得每次匹配失败后,下一次匹配开始的位置尽可能的向后移。例如上面的例子A,如果用KMP算法,在第三次匹配后就可以跳过第四次匹配,直接进行第五次匹配。(i 不需要回退到4)

KMP的基本流程

下面我们具体来实现这种算法:

再次解释一下

  • 指针 i :主串中当前比较的元素位置
  • 指针 j :子串中当前比较的元素位置
Created with Raphaël 2.2.0 开始 i = 1, j = 1 A[i] == B[j]? 全部匹配成功了吗? 结束 j++ next函数 yes no yes no
Created with Raphaël 2.2.0 next函数确定i, j 传入i, j j == 0? i++, j++ (j不能再退了) 结束,输出i, j j = next[j] (j还能再退) yes no

通过比较我们发现KMP算法和原始算法的区别就是在匹配失败的时候i不用回退(i -= j + 2),通过next[]这个数组确定下一个j值就可以。

所以KMP算法的核心还是求出子串的next[]数组

下面给出next[j]的定义

j==1
j!=1
next[j]
0
B[j]前面子串的前缀和后缀相同时的最长长度+1
如何理解next这个函数呢?

下图表示模式匹配时的子串B,
一个例子
如图,当我们比较到D的开头时,发现与主串不匹配。因为我们已经比较过一次C和主串中对应位置是相等的,而A又与C相等,所以我们直接从B的开头匹配就行了(B的开头也就是A或C的长度+1)。

求next数组

求的时候我们可以用递推的方法:

设next[j] = m 如何求next[j+1]?
首先next[j] = m 表示 B[1] ~ B[m-1]与 B[j-m+1] ~ B[j - 1]是相等的
而当j 滑动到j+1处时,有两种情况:

  1. B[m] == B[j]
    此时直接累加m的值即可
  2. B[m] != B[j]
    此时就要向前寻找下一个B[m],使B[m] == B[j]。令m = next[m],再次判断。(这个地方和上面KMP的算法有点像)

举个例子
a b c a b d a a a
0 1 1 1 2 3 1 2
next[8] = 2 表示B[1] == B[8]
比较B[2]和B[9],发现不相等
m = next[2] = 1
比较B[1]和B[9],发现相等。
累加m的值为2,即next[9] = 2

next求法代码表示

	
	next[1] = 0;
	m = 0;				//表示子串中的相同前后缀最长长度+1(初始为0)
	j = 1;				//表示扫描时的位置
	while(j < B_len)
	{
		if(m == 0 || B[m] == B[j])		
		{
			m++;
			j++;
			next[j] = m;
		}
		else
			m = next[m];
	}

总结

KMP算法的理论大概就这样,上面的流程图为了保证简洁省略了一些特殊情况,主要还是有助于理解,实际程序自己写一写就好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值