KMP字符串模式匹配详解(四)

五.其他表示模式值的方法
上面那种串的模式值表示方法是最优秀的表示方法,从串的模式值我们可以得到很多信息,以下称为第一种表示方法。第二种表示方法,虽然也定义 next[0]= -1, 但后面绝不会出现 -1 ,除了 next[0] ,其他模式值 next[j]=k(0 k<j) 的意义可以简单看成是:下标为 j 的字符的前面最多 k 个字符与开始的 k 个字符相同,这里并不要求 T[j] != T[k] 。其实 next[0] 也可以定义为 0 (后面给出的求串的模式值的函数和串的模式匹配的函数,是 next[0]=0 的),这样, next[j]=k(0 k<j) 的意义都可以简单看成是:下标为 j 的字符的前面最多 k 个字符与开始的 k 个字符相同。第三种表示方法是第一种表示方法的变形,即按第一种方法得到的模式值,每个值分别加 1 ,就得到第三种表示方法。第三种表示方法,我是从论坛上看到的,没看到详细解释,我估计是为那些这样的编程语言准备的:数组的下标从 1 开始而不是 0
  下面给出几种方法的例子:
      表一。
下标
0
1
2
3
4
5
6
7
8
T
a
b
a
b
c
a
a
b
c
(1) next
-1
0
-1
0
2
-1
1
0
2
(2) next
-1
0
0
1
2
0
1
1
2
(3) next
0
1
0
1
3
0
2
1
3
第三种表示方法 , 在我看来,意义不是那么明了,不再讨论。
           表二。
下标
0
1
2
3
4
T
a
b
c
A
c
(1)next
-1
0
0
-1
1
(2)next
-1
0
0
0
1
      表三。
下标
0
1
2
3
4
5
6
7
T
a
d
C
a
d
C
a
d
(1)next
-1
0
0
-1
0
0
-1
0
(2)next
-1
0
0
0
1
2
3
4
 
对比 串的模式值第一种表示方法和第二种表示方法,看表一:
第一种表示方法 next[2]= -1, 表示 T[2]=T[0] ,且 T[2-1] !=T[0]
第二种表示方法 next[2]= 0, 表示 T[2-1] !=T[0], 但并不管 T[0] T[2] 相不相等。
第一种表示方法 next[3]= 0, 表示虽然 T[2]=T[0] ,但 T[1] ==T[3]
第二种表示方法 next[3]= 1, 表示 T[2] =T[0], 他并不管 T[1] T[3] 相不相等。
第一种表示方法 next[5]= -1, 表示 T[5]=T[0] ,且 T[4] !=T[0] T[3]T[4] !=T[0]T[1] T[2]T[3]T[4] !=T[0]T[1]T[2]
第二种表示方法 next[5]= 0, 表示 T[4] !=T[0] T[3]T[4] !=T[0]T[1] T[2]T[3]T[4] !=T[0]T[1]T[2] ,但并不管 T[0] T[5] 相不相等。换句话说:就算 T[5]==’x’, T[5]==’y’,T[5]==’9’, 也有 next[5]= 0
从这里我们可以看到:串的模式值第一种表示方法能表示更多的信息,第二种表示方法更单纯,不容易搞错。当然,用第一种表示方法写出的模式匹配函数效率更高。比如说,在串 S= adCadCBdadCadCad 9876543 ”中匹配串 T= adCadCad , 用第一种表示方法写出的模式匹配函数 , 当比较到 S[6] != T[6] 时,取 next[6]= -1 (表三) , 它可以表示这样许多信息: S[3]S[4]S[5]==T[3]T[4]T[5]==T[0]T[1]T[2] ,而 S[6] != T[6] T[6]==T[3]==T[0] ,所以 S[6] != T[0], 接下来比较 S[7] T[0] 吧。如果用第二种表示方法写出的模式匹配函数 , 当比较到 S[6] != T[6] 时,取 next[6]= 3 (表三) , 它只能表示: S[3]S[4]S[5]== T[3]T[4]T[5]==T[0]T[1]T[2] ,但不能确定 T[6] T[3] 相不相等,所以,接下来比较 S[6] T[3]; 又不相等,取 next[3]= 0 ,它表示 S[3]S[4]S[5]== T[0]T[1]T[2] ,但不会确定 T[3] T[0] 相不相等,即 S[6] T[0] 相不相等,所以接下来比较 S[6] T[0] ,确定它们不相等,然后才会比较 S[7] T[0] 。是不是比用第一种表示方法写出的模式匹配函数多绕了几个弯。
为什么,在讲明第一种表示方法后,还要讲没有第一种表示方法好的第二种表示方法?原因是:最开始,我看严蔚敏的一个讲座,她给出的模式值表示方法是我这里的第二种表示方法,如图:
她说:“ next 函数值的含义是:当出现 S[i] !=T[j] 时,下一次的比较应该在 S[i] T[next[j]]  之间进行。”虽简洁,但不明了,反复几遍也没明白为什么。而她给出的算法求出的模式值是我这里说的第一种表示方法 next 值,就是前面的 get_nextval() 函数。匹配算法也是有瑕疵的。于是我在这里发帖说她错了:
   现在看来,她没有错,不过有张冠李戴之嫌。我不知道,是否有人第一次学到这里,不参考其他资料和明白人讲解的情况下,就能搞懂这个算法(我的意思是不仅是算法的大致思想,而是为什么定义和例子中 next[j]=k(0 k<j) ,而算法中 next[j]=k(-1 k<j) )。凭良心说:光看这个讲座,我就对这个教受十分敬佩,不仅讲课讲得好,声音悦耳,而且这门课讲得层次分明,恰到好处。在KMP这个问题上出了点小差错,可能是编书的时候,在这本书上抄下了例子,在那本书上抄下了算法,结果不怎么对得上号。因为我没找到原书,而据有的网友说,书上已不是这样,也许吧。说起来,教授们研究的问题比这个高深不知多少倍,哪有时间推演这个小算法呢。总之,瑕不掩玉。
书归正传,下面给出我写的求 第二种表示方法表示的模式值的函数 , 为了从 S 的任何位置开始匹配 T ,“当出现 S[i] !=T[j] 时,下一次的比较应该在 S[i] T[next[j]]  之间进行。”     定义 next[0]=0
  v oid myget_nextval(const char *T, int next[])
{
     // 求模式串 T next 函数值(第二种表示方法)并存入数组 next                 
     int j = 1, k = 0;
     next[0] = 0;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值