KMP算法---理解

来源:互联网 发布:网络口碑营销经典案例 编辑:程序博客网 时间:2024/05/16 01:19

一.  简单匹配算法

         简单匹配算法,又称为朴素的模式匹配算法。简单匹配算法回溯的是i,不是j。

先来看一个简单匹配算法的例子:

public class KMP_test {
/*
* 若串 S 中从第pos(S 的下标0≤pos<StrLength(S))个字符 起存在和串 T 相同的子串,则称匹配成功,返回第一个
* 这样的子串在串 S 中的下标,否则返回 -1
*/
public static int find(String src, String target, int i) {
char[] sc = src.toCharArray();
char[] tc = target.toCharArray();
int j = 0;
  while ((i + j) < sc.length && j < tc.length) {
    if (sc[i + j] == tc[j]) {
            j++;// 继续比较后一字符
      }  else {
               i++; // 重新开始新的一轮匹配,i+1,j归零
                 j = 0;
          }
         }

     if (j == tc.length)
     return i; // 匹配成功 返回下标
else
        return -1;// 串S中(第pos个字符起)不存在和串T相同的子串
  }
public static void main(String[] args) {
String src = "keylovktyuy";
String target = "vkty";
     System.out.println(find(src, target, 0));
// true代表找到了,false代表木有找到,
    System.out.println(find(src, target, 0) == -1 ? false : true);
}
}


此算法的思想是直截了当的:将主串S中某个位置i起始的子串和模式串T相比较。即从 j=0 起比较 S[i+j] 与 T[j],若相等,则在主串 S 中存在以 i 为起始位置匹配成功的可能性,继续往后比较( j逐步增1 ),直至与T串中最后一个字符相等为止,否则改从S串的下一个字符起重新开始进行下一轮的"匹配",即将串T向后滑动一位,即 i 增1,而 j 退回至0,重新开始新一轮的匹配。即不断回溯的是i,不是j。

例如:在串S=”abcabcabdabba”中查找T=” abcabd”(我们可以假设从下标0开始):先是比较S[0]和T[0]是否相等,然后比较S[1] 和T[1]是否相等…我们发现一直比较到S[5] 和T[5]才不等。如图:

不知为什么,图没显示

当这样一个失配发生时,T下标必须回溯到开始,S下标回溯的长度与T相同,然后S下标增1,然后再次比较。如图:

这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:

又一次发生了失配,所以T下标又回溯到开始,S下标增1,然后再次比较。这次T中的所有字符都和S中相应的字符匹配了。函数返回T在S中的起始下标3。如图:

二. KMP匹配算法

     利用前面比较的结果,得出一个next数组或nextval数组(后者是前者的改良版),此数组的值为j的回溯值。即KMP算法回溯的是j,不是i。

      1  next数组的求法:

             1.1 理解思路: 
                          a  当 j=1,next[ j]=0 ;             
                          b  当 j=j,时,得到模式串的0---(j-1)的子串,求的该子串的前缀与后缀的最长公共串,则next[ j]=最长公共串+1;
                          c   其它情况,next[ j ]=1 ;            
                      例 如:       T=“a b a b a a a b a”
                                             j    1 2 3 4 5 6 7 8 9
                                 next [ j ]   0 1 1 2 3 4 2 2 3
                                 当j=6时,得到T的1--5的子串ababa,则ababa 的前缀为:a,ab,aba,abab      
                                                                                                                     的后缀为:baba,aba,ba,a       
                                                    前缀与后缀的最长公共串为aba,则next[ 6]=3+1=4
              1.2代码实现: 
                            void get_next(String T,int *next){   
                                     int i,j;
                                      i=1;
                                      j=0;
                                      next[1]=0;    
                              while(i<T.length()){
                                          if(j==0||T[i]==T[j]){   // T[ i]表示后缀的单个字符
                                              ++i;                     // T[ j]表示前缀的单个字符
                                            ++j;
                                             next[ i]=j;
                                            }else{
                                                j=next[ j];  // 若字符不相同,则j值回溯
                                                       }
                                }

           2 nextval数组的求法:

                  
                   当串的后面的字符都与首位的字符相等时,可以用首位的next[ 1 ]的值去取代与之相等的字符的netx[ j ]的值。
               
                 while(i<T.length()){
                                          if(j==0||T[i]==T[j]){   // T[ i]表示后缀的单个字符
                                              ++i;                     // T[ j]表示前缀的单个字符
                                              ++j;
                                             next[ i]=j;
                                            if(T[ i]!=T[j])
                                                     nextval[ i]=j;   // 当前字符与前缀字符不同,则当前的j为nextval在i位置的值
                                            else
                                                    nextval[ i]=  nextval[ j] //若相同,则将前缀字符的nextval值付给nextval在i位置的值
                                            }else{
                                                      j=next[ j];  // 若字符不相同,则j值回溯
                                                       }
                                }

             3  KMP算法  (KMP算法回溯的是j,不是i。


                      while(i<s.length&&j<t.length){
                                  if(j==0||s[ i]==t[ j]){
                                            ++i;
                                            ==j;                          
                                      }else{
                                                   j=next[ j];  // j=nextval[ j];  j退回到合适的位置,i值不变
                                            }
                         }

参考:
http://blog.csdn.net/niushuai666/article/details/6960985