数据结构——串的朴素模式和KMP匹配算法

http://blog.csdn.net/dawanganban/article/details/41786477

一、朴素模式

假设我们要从主串S=”goodgoogle"中找到子串T=“google"的位置,步骤如下:


i表示主串的当前位置下标,j表示子串的当前位置下标,如上图在第一轮比较(i=1开始)中j=4和i=4的位置不匹配,接下来就要指针回退,从i=2开始比较,如下:


如此反复直到比较到 i =(主串长度-子串长度+1)的位置或者 j = 子串的长度 就退出比较循环,上面的主串和子串在比较到i=5的位置就完全匹配了。


  1. #include <stdio.h>  
  2.   
  3. int Index(const char S[], const char T[], int pos){  
  4.     int i = pos; //主串当前下标  
  5.     int j = 1;   //子串当前下标  
  6.     //S[0]是主串的长度,T[0]是子串的长度  
  7.     while(i <= S[0] && j <= T[0]){  
  8.         //如果相等则继续向下比较  
  9.         if(S[i] == T[j]) {  
  10.             ++i;  
  11.             ++j;  
  12.         //如果不等则指针回退  
  13.         }else{  
  14.             i = i - j + 2; //主串回退  
  15.             j = 1;  
  16.         }  
  17.     }  
  18.     //j>T[0]则说明子串被完全匹配  
  19.     if( j > T[0]) return i - T[0];  
  20.     else return 0;  
  21. }  
  22.   
  23. int main(){  
  24.     char S[] = {10, 'g''o''o''d''g''o''o',  
  25.         'g''l''e'};   
  26.     char T[] = {6, 'g''o''o''g''l''e'};  
  27.     int i = Index(S, T, 1);  
  28.     printf("%d\n", i);  
  29.   
  30.     return 0;  
  31. }  
分析一下这种匹配算法最好的情况时间复杂度是O(1)只需要一次比较,最坏的情况是每次最后一个字符不匹配,时间复杂度是O(m*n) m是主串长度n是子串的长度。

二、KMP算法

像二进制这样的多个0和1重复的字符串,上面的模式匹配需要挨个遍历是非常慢的,KMP算法可以大大避免重复遍历的情况。

下面我们来看看KMP算法的基本原理


如上,可以看到主串S和子串T在第一轮比较的时候,前面5个相等,只有i=6和j=6的位置不等。由于子串T中abcde这5个字符本身互不相等,可以知道子串T中a就不可能和j=2、3、4、5的位置的字符相等。所以可以直接跳到i=6的位置进行比较。


再看上图,如果子串T中存在重复的元素(比如j=1,2和j=4,5处的字符),按照上面的分析,我们可以直接跳到i=4的位置比较,但是我们已经知道j=1,2和j=4,5相等,并且i=4,5和j=4,5相等,所以可以不用比较i=4,5和j=1,2。

KMP模式匹配算法就是为了不让i指针回退,既然i值不回退,我们就要考虑变化j的值了。通过上面的观察可以发现,j值的变化与主串其实没有什么关系,而是取决于子串T中是否有重复问题。

我们把T串各个位置的j值的变化定义为一个数组next,那么next的长度就是T串的长度,可以得到下面函数:


  1. #include <stdio.h>  
  2.   
  3. void get_next(const char T[], int *next){  
  4.       
  5.     int i,j;  
  6.     i = 1;  
  7.     j = 0;  
  8.     next[1] = 0;  
  9.     //T[0]是子串T的长度  
  10.     while(i < T[0]){  
  11.         //T[i]表示后缀的单个字符  
  12.         //T[j]表示前缀的单个字符  
  13.         if( j==0 || T[i] == T[j]){  
  14.             ++i;  
  15.             ++j;  
  16.             next[i] = j;  
  17.         }else{  
  18.             j = next[j];  
  19.         }  
  20.     }  
  21. }  
  22.   
  23. int Index_KMP(const char S[], const char T[], int pos){  
  24.     int i = pos;  
  25.     int j = 1;  
  26.     int next[255];  
  27.     get_next(T, next);  
  28.   
  29.     while(i <= S[0] && j <= T[0]){  
  30.         //相对于朴素算法,增加了一个j==0的判断  
  31.         if( j==0 || S[i] == T[j]){  
  32.             ++i;  
  33.             ++j;  
  34.         }else{  
  35.             //j回退到合适的位置,i的值不变  
  36.             j = next[j];  
  37.         }  
  38.     }  
  39.     if( j>T[0]){  
  40.         return i-T[0];  
  41.     }else{  
  42.         return 0;  
  43.     }  
  44. }  
  45. int main(){  
  46.   
  47.     return 0;  
  48. }  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值