KMP算法

推荐大牛讲解KMP的文章,比我讲的好10000倍,就是长一点:  http://blog.csdn.net/v_july_v/article/details/7041827

主串:    a b a b c a b c a c b a b
匹配串:    a b c a c 

i:主串当前下标
j:匹配串当前下标

第一趟匹配:    a    b    a    b    c    a    b    c    a    c    b    i = 3
                      a    b    c    a    c                                         j = 3

第二趟匹配:    a    b    a    b    c    a    b    c    a    c    b    i = 7
                                   a    b    c    a    c                            j = 5

第三趟匹配:    a    b    a    b    c    a    b    c    a    c    b    i = 7
                                                    (a)  b    c    a    c          j = 2


我们看第三趟匹配直接从 b 处开始,这是利用了第二趟匹配的结果。

1:在第二趟匹配在 c 处失败后,按照常规应该是从主串下一位即i=4,j=1开始匹配,依次类推。
2:但是由于和主串中的abca是匹配的,注意:abca 
3:因为a != b != c;  但是 a = a;所以直接从b开始匹配就好了。
4:注意对于在第三趟匹配中。j=1的(a)所在位置i=7 不正是 第二趟匹配中 j = 4的a所在的位置吗。

看出来没: 每次选择下一趟匹配时,(a)占了a的位置,为什么呢。 因为他两个相等阿。额,废话。

5: 所以对于KMP算法: 如在 p = a b c a c 中如在 j=5出发生失配时,选择下一步 j=k 的其实是求对于
sn = a b c a中的k值。 使得s0,s1...s(k-1) = sn,s(n-1),...,s(n-k+1) = p(j-k+1)...p(j-1)(j=5)

 在这里 a = a 所以 在j=5处失配时, k(j=5) = 2;(即当模式串在j=5 处失配时,存j=k=2处在和主串比较。

再设想如果 P = a b c c b 当在j = 6即b 处失配时 k=3 = k(j=5) + 1;

在下面中,P 相当于 b c b 。 当j = 5=c时。 P1P2……P(k-1) = aP(j-k+1)……P(j-1) = a;

当j = j+1 = b时。 P1P2……P(k-1)Pk = b;  P(j-k+1)……P(j-1)Pj = b

-----------------------------------------------------------------------------------------------
1:设模式串P = P1P2….Pk’……P(k-1)Pk……P(j-k+1)……P(j-1)P(j+1)……Px 
设主串S; 

int i = 主串中位置. int j=模式串中位置; (从1开始计数) 
int k = 模式串在j处”失配”后的滑动位置. 

2:KMP算法推导: 
1
2
3
4
5
while(i < S.length && j < T.length){ 
if(P[j] != p[i]){ //也就是主串中的第i个字符和模式串中的第j个字符”失配”. 
//按照一般的方法,应该{ j=1;}从主串的下一个字符位置开始重新比较. 
//根据KMP算法(即根据以前比较的结果){ j=k ;} 从P[k]开始和主串比较. 
}
所以有: P1……Pk = S(i-k+1)……S(i-1) = P(j-k+1)……P(j-1) 
所以k值变得只和模式串自己有关—->我们就可以计算出模式串在每个位置失配后的K值,然后用这些值来进行字符串匹配. 
所以有模式串函数next:

3:根据next函数计算next[]数组. 


设模式串P = P1P2…..Px…..Pk’….P(k-k’+1)….P(k-1)Pk……P(j-k+1)……P(j-1)PjP(j+1)……Px 


如果P 在 j 处发生了失配,对应为next[j]=k.所以有: P1……P(k-1) = P(j-k+1)……P(j-1) 


next[1] = 0;


     
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//如果P在j+1处发生了失配: 
if (Pk = Pj) {
     P1......P(k- 1 )Pk = P(j-k+ 1 )......P(j- 1 )Pj 
     //所以:next[j+1] = next[j]+1 = k+1;}
if (Pk != Pj){
     //所以要找P1P2...Px = P(j-x+1).....P(j-1)Pj
     //怎么找这个x呢:
 
     //可以设想模式串为: P2 = P(j-x+1).....P(j-1)Pj要在P中进行模式匹配,现在k处发生失配,所
     //以要从P2[k'=next[k]]处来进行下一步匹配了.   
     //所以有P1...P(k'-1) = P(k-k'+1)...P(k-1) =  P(j-k'+1)...P(j-1)
     if (P[k'] = P[j]) {
         P1...P(k '-1)Pk'  = P(j-k'+ 1 )...P(j- 1 )Pj
         //所以: next[j+1] = next[k]+1 = k'+1
     } else {
         //重复上述过程,求k".....,如果结果不存在,next[j+1] = 1
     }
}

伪代码如下: 

1
2
3
4
5
6
7
8
int [] getNext(String t){ 
int [] next ; 
j=  1 ; next[ 1 ] =  0 ;k=  0 ; //next数组从1开始数,即从next[1]开始
while (j <= t.length){
      if (k== 0  || p[j] = p[k]){ j++;k++;  next[j] = k; }  
      else  k = next[k];
}

4:改进 
next[] = 0 1 2 3 4 
设模式串: P = a a a a b (int j) 
设主串: S = a a a b a a a a b (int i)

可以看到P[4] != S[4] 所以next[4]=3将P向右滑动如下:

S = a a a b a a a a b
P =   a a a a b

但是由于P[4] = P[3] = P[3] = P[1],其实可以直接滑动如下:
S = a a a b a a a a b
P =         a a a a b

这就是说: 当在j=4处发生失配时,取j = next[4].但是P[next[4]] = P[4]
所以肯定:S[i] != P[next[4]] 依次类推 就要取 j = next[j]
所以发现S[i] != P[next[4]] 这个是已知的,因为P[next[4]] = P[4],所以不需要比较S[i] 和 P[next[4]].

也就是说 next数组中.可以直接让 next[4] = next[next[4]],依次类推
新的next[] = 0 0 0 0 4

所以新的为代码如下:  

       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
int [] getNext(String t){
int [] next ;
     j=  1 ; next[ 1 ] =  0 ;k=  0 ; //next数组从1开始数,即从next[1]开始
  while (j <= t.length){
  if (k== 0  || p[j] = p[k]){
       j++;k++; 
     if (p[j] != p[k])  next[j] = k; 
     else  next[j] = next[k];
}  
  else  k = next[k];
 
}
 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值