我也来写KMP

来源:互联网 发布:清华北大碾压知乎 编辑:程序博客网 时间:2024/06/05 16:34

网上看了很多关于KMP算法,唉,毕竟不是中国人自己写出来的东西,所以有的人乱写一通(当然,在您眼中,我或许也是),比如KMP算法中有一个公式,对着这个公式进行得出next数组的值,比如下面:

(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= -1   意义:模式串T中下标为j的字符,如果与首字符
相同,且j的前面的1—k个字符与开头的1—k
个字符不等(或者相等但T[k]==T[j])(1≤k<j)。
如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6]
(3)next[j]=k    意义:模式串T中下标为j的字符,如果j的前面k个
字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
                       即T[0]T[1]T[2]。。。T[k-1]==
T[j-k]T[j-k+1]T[j-k+2]…T[j-1]
且T[j] != T[k].(1≤k<j);
(4) next[j]=0   意义:除(1)(2)(3)的其他情况。

 

我个人觉得如果是要对照公式的话,KMP还有什么意义呢(噢噢噢,理解可以方便一点)?

下面我也在网上找了些资料,ctrl+C和ctrl_V一下,然后再自己修改下。

先贴上代码吧,不一定对。

 

 

 

再说下思路:

构造next数组,next数组含义:next[j]表示j前面出现的K个字符(不包括K本身),与下表为0起的K个字符相同,数学表达即:

T[0]T[1]...T[k-1] 与 T[j-k]...T[j-1]是相同的,所以

next[0]=-1,next[1]=0.....

(next数组的含义要想充分了解的话,做这两个题:PKU 2406 和 PKU 1961,包准你事半功倍!!!)。

然后nextVal数组,是为了与next数组想区别,也可以称作修正next数组或者改进next数组,我们可以看到:

当我们分析到模式串中的第i位于目标串不同时 求得应该从K位开始比较 但是如果第K位于第i位相同的时候,第K位一定与目标串也不同因此应该再次改成当K位比较不同时的NEXT值 即 NEXT[K]。

(以下划横线的是需要证明的,没兴趣的同学可略过)
(转自:http://www.cppblog.com/sicheng/archive/2006/10/10/13537.html

由于NEXT是由左向右推出来的 所以只需修正一次 (自己想着证明吧)

另外,程序没有使用一个数组来保存未修正时的NEXT值 而直接用J来充当这个功能 但是J是由NEXT(已修正)得来的 那么有没有可能J得不到正确的值?
这个问题最先是我自己想的 后来验证了一下 J一定能够得到正确的值
下面我们来分析一下
下标                         0  1  2  3  4  5  6  7  8  9 10 11 1213
目标串                     0  1  0  0  1  0  1  0  1  0  0   0   0  1
j(未修正的next)       -1 0  0  1  1  2  3  2  3  2  3   4   1  1
修正了的next          -1 0 -1  1  0 -1  3 -1 3 -1  1  4   1   0
让我们看看但模式串比较到了12位的时候(下标为11)
next[11] = 4; next[4] = 0; j|4 = 1...
这时候next[4]拿到的并不是正确的J值!!!
这会导致什么?会导致略过一个比较 即p[11]与p[1]的比较被略过!直接比较了p[11]与p[0]!
是否可以略过这个比较?!
答案是肯定的.
根据逻辑推理 如果p[11]与p[4]不等 而p[4] = p[1]那么p[11]与p[1]一定不等 所以可以略过
这样说似乎很牵强 我们再回想一下NEXT数组的意义 即当我们比较到i位时不等 应该由哪一位重新开始比较 而实际上 这个过程相当于在 7-11的这个串中查找0-3!所以根据NEXT数组的意义 我们也可以知道 这里并不会导致错误 由于被略去的比较一定不需比较 因此 J始终可以得到正确的未修正的NEXT值!

 

 

好吧,下面我们来列举其他例子:

(转自:http://www.slyar.com/blog/kmp-next-nextval.html)

 

计算前缀 Next[i] 的值:

next[0] = -1;定值。
next[1] = 0;s[1]前面没有重复子串。
next[2] = 0;s[2]前面没有重复子串。
next[3] = 0;s[3]前面没有重复子串。
next[4] = 1;s[4]前面有重复子串s[0] = 'a'和s[3] = 'a'。
next[5] = 2;s[5]前面有重复子串s[01] = 'ab'和s[34] = 'ab'。
next[6] = 3;s[6]前面有重复子串s[012] = 'abc'和s[345] = 'abc'。
next[7] = 4;s[7]前面有重复子串s[0123] = 'abca'和s[3456] = 'abca'。

计算修正后的 Nextval[i] 值:

nextval[0] = -1;定值。
nextval[1] = 0;s[1] != s[0],nextval[1] = next[1] = 0。
nextval[2] = 0;s[2] != s[0],nextval[2] = next[2] = 0。
nextval[3] = -1;s[3] == s[0],nextval[3] = nextval[0] = -1。
nextval[4] = 0;s[4] == s[1],nextval[4] = nextval[1] = 0。
nextval[5] = 0;s[5] == s[2],nextval[5] = nextval[2] = 0。
nextval[6] = -1;s[6] == s[3],nextval[6] = nextval[3] = -1。
nextval[7] = 4;s[7] != s[4],nextval[7] = next[7] = 4。

 

好了,KMP要想完全理解,真的不是一朝一夕之功,好运~

 

 

 

原创粉丝点击