赞
踩
事实上这三种“next数组”的结果都没有错,只是他们涉及到了KMP匹配过程的两种理解。在后文中将说明这三者之间的区别、转换以及所属的KMP匹配过程的演示。
第一部分 KMP算法与next数组的基本认识
设主串为T,带搜索的子串为S,i表示T遍历到了哪个字符,j表示S遍历到了哪个字符:
暴力查找方法通常会在T[i]!=S[j]时,j和j都回退,而KMP的核心就是主串不回退,即i只增不减,j相应的改变。而j依何改变?这就需要提前处理T子串,生成相应的next数组(这里所说的next数组其实并不完善,后文中将会提到改良版的nextval数组)。
那么next数组代表的是什么意思呢?通俗点说就是,这次没配上,下次找谁配。next数组里存的就是下次交配对象…不对,是匹配对象…
正是因为KMP只预处理子串,因此很适合这样一种问题的求解:给定一个S子串,和一群不同的T主串,问S是哪些T的子串。
第二部分 KMP算法的两种匹配过程
同我一样迷惑于许多博客中讲述的“互相矛盾的”匹配过程的童鞋请重点看这一部分,接下来我将说明,为何明明很好理解的主串不回退匹配过程会有那么多不同的声音,因为它们根..本..不..是..一..个..过..程..!!!
这也是为什么关于next数组会有那么多版本的不同解释,先约束下,本文中用到的数组以1开始(以0开始也一样整体减1就好了,网上next数组第一位为-1的就是数组以0开始的)。
下表中Lmax代表失配字符上一位字符所对应部分子串的最大前后缀共有元素长度,也就是部分匹配值,next代表存在一些瑕疵的next数组,nextval代表改良版的next数组。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
S | a | b | a | a | b | c | a | c |
Lmax | 0 | 0 | 1 | 1 | 2 | 0 | 1 | 0 |
next | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
nextval | 0 | 1 | 0 | 2 | 1 | 3 | 0 | 2 |
1.部分匹配值法
推荐博客:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 详细且图文并茂(只可惜没有next数组什么事),能够帮助大家快速理解KMP到底要干什么。
//伪代码如下
Lmax[1]=0;
j=0;
for i=2 to n do{
while j>0 and S[j+1]!=S[i]
do j=Lmax<span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">[j];</span>
if S[j+1]=S[j]
then j++;
Lmax[i]=j;
}
//伪代码如下
i=1;
j=0;
nextval[1]=0;
while i<S[0]{
whlie j>0 and S[i]!=S[j]
do j=nextval[j];
i++;
j++;
if S[i]=S[j]
then nextval[i]=nextval[j];
else
then nextval[i]=j;
}
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
S | a | b | a | a | b | c | a | c |
Lmax | 0 | 0 | 1 | 1 | 2 | 0 | 1 | 0 |
next | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
nextval | 0 | 1 | 0 | 2 | 1 | 3 | 0 | 2 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
S | a | b | a | a | b | c | a | c |
Lmax | 0 | 0 | 1 | 1 | 2 | 0 | 1 | 0 |
next | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
nextval | 0 | 1 | 0 | 2 | 1 | 3 | 0 | 2 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。