算法系列之KMP算法

来源:互联网 发布:地理信息系统软件face 编辑:程序博客网 时间:2024/05/18 01:23

http://c610367182.iteye.com/blog/1947316

串的模式匹配算法 

模式匹配是指将两个模式作为输入,计算模式元素之间语义上的对应关系的过程,在数据结构中模式匹配是字符串的基本运算之一。 
有两个字符串S和T,字符串S称为正文(被匹配字符串),字符串T称为模式(匹配字符串),要求找出模式T在正文S中的首次出现的位置。一旦模式T在正文S中找到,就说发生一次匹配。 
示例  目标 S : “Beijing” 
         模式 T : “jin” 
         匹配结果 = 3
 
首先来说说一般情况下的匹配模式:在字符串T跟字符串P上分别定义两个指针i,j,从0开始,如果S[i]==T[j],i++;j++;指针下移,继续循环,一直到字符串T或者字符串P结束,如果匹配成功,返回i-j;如果中间出现S[i]!=T[j],则i回溯(i=i-j+1),而j也回溯(j=0),继续循环。还是图比较好理解,见下图,从左到右(i=5-5+1=1,下图属于笔误,谢谢网友提醒) 

代码如下(java实现):
Java代码  收藏代码
  1. //------------串的模式匹配原声算法----------------  
  2.     public void index(char[] test,char[] pattern) {  
  3.         int test_len=test.length;  
  4.         int pattern_len=pattern.length;  
  5.         int i=0,j=0;  
  6.         while (i<test_len) {  
  7.             if (j==pattern_len) {  
  8.                 System.out.println("配对成功:起始下标(从0开始)为---"+(i-j));  
  9.                 break;  
  10.             }  
  11.             if (test[i]==pattern[j]) {  
  12.                 i++;j++;  
  13.             }else {  
  14.                 if (j==0) {  
  15.                     i++;j++;  
  16.                 }else {  
  17.                     i=i-j+1;  
  18.                     j=0;  
  19.                 }  
  20.             }  
  21.         }  
  22.     }  
  23.     public static void main(String[] args) {  
  24.         char[] test="abcabcabdaaaaa".toCharArray();  
  25.         char[] pattern="abcabd".toCharArray();  
  26.         KMP kmp=new KMP();  
  27.         kmp.index(test, pattern);  
  28.     }  


改进算法---KMP算法 
百度百科:kmp算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是根据给定的模式串W1,m,定义一个next函数。next函数包含了模式串本身局部匹配的信息。 
效果图如下: 

至于j要回溯多少则关系到next函数值的求解:getNext() 
       1.前两位必定为-1和0; 
       2.计算第三位(下标为2)的时候,看第二位b的next值,为0,则把b和0下标对应的a进行比较,不同,则第三位a的next的值为0; 
       3.计算第四位(下标为3)的时候,看第三位c的next值,为0,则把c和0下标对应的a进行比较,不同,则第三位a的next的值为0; 
       4.计算第五位(下标为4)的时候,看第四位a的next值,为0,则把a和0下标对应的a进行比较,相同,则第五位b的next值为第四位a的next值加上1,为1,因为是在第四位实现了其next值对应的值与第五位相同。 
       5.计算第六位(下标为5)的时候,看第五位b的next值,为1,则把b和1下标对应的b进行比较,相同,则第六位c的next值为第五位b的next值加上1,为2,因为是在第五位实现了其next值对应的值与第五位相同。
 
代码实现KMP算法: 
Java代码  收藏代码
  1. package test.aglorith;  
  2. import java.util.Arrays;  
  3. public class KMP {  
  4.     //----------------KMP------------------------------  
  5.     public void indexByKMP(char[] test,char[] pattern) {  
  6.         int test_len=test.length;  
  7.         int pattern_len=pattern.length;  
  8.         int[] next=getNext(pattern);  
  9.         int i=0,j=0;  
  10.         while (i<test_len) {  
  11.             if (j==pattern_len) {  
  12.                 System.out.println("配对成功:起始下标(从0开始)为---"+(i-j));  
  13.                 break;  
  14.             }  
  15.             if (test[i]==pattern[j]) {  
  16.                 i++;j++;  
  17.             }else {  
  18.                 if (j==0) {  
  19.                     i++;j++;  
  20.                 }else {  
  21.                     //i=i-j+1;  
  22.                     j=next[j];  
  23.                 }  
  24.             }  
  25.         }  
  26.     }  
  27.     public int[] getNext(char[] pattern) {  
  28.         int pattern_len=pattern.length;  
  29.         int[] next=new int[pattern_len];  
  30.         next[0]=-1;next[1]=0;  
  31.         for (int i = 2; i < pattern_len; i++) {  
  32.             int j=i;  
  33.             while(j>1) {  
  34.                 if (pattern[i-1]==pattern[next[j-1]]) {  
  35.                     next[i]=next[j-1]+1;  
  36.                     break;  
  37.                 }else {  
  38.                     j=next[j-1];  
  39.                 }  
  40.             }  
  41.             if (j==1) {  
  42.                 next[i]=1;  
  43.             }  
  44.             //System.out.println(Arrays.toString(next));  
  45.         }  
  46.         System.out.println(Arrays.toString(next));  
  47.         return next;  
  48.     }  
  49.     public static void main(String[] args) {  
  50.         char[] test="abcabcabdaaaaa".toCharArray();  
  51.         char[] pattern="abcabd".toCharArray();  
  52.         KMP kmp=new KMP();  
  53.         kmp.getNext(pattern);  
  54.         kmp.indexByKMP(test, pattern);  
  55.     }  
  56. }  

至于next函数值的求解原理至今还没有真正想透彻,希望广大网友多多指教! 
经网友推荐的博客文章:http://www.cppblog.com/oosky/archive/2006/07/06/9486.html 
突然缠绕我心中的那道弯就绕过了,之前在想: 
求解T[i]时,T[i-1]与T[next[i-1]]不同时,T[i-1]要与T[next[next[i-1]]]比较;原因是一个词,“比较过了”。还是画图解释(不知道大家看不看得懂):