KMP算法字符串查找子串

题目:

经典的KMP算法

分析:

和KMP算法对应的是BF算法,其中BF算法时间复杂度,最坏情况下可以达到O(n*m),而KMP算法的时间复杂度是O(n + m),所以,KMP算法效率高很多。

但是KMP算法不太好理解,其中牵涉到next数组,目标就是让模式串尽可能的往右滑动,减少比较次数,比如

a  b  a  b  c

-1 0  0  1  2

比如我们比较ababc时,如果c比较发现错误,前面的abab已经比较成功,那么下次比较,我们只需要从aba的最后一个a开始比较,这样省去了从头开始比较。

算法代码:

  1. #include <iostream>  
  2. #include <cstdlib>  
  3. #include <cstdio>  
  4. using namespace std;  
  5.   
  6. //这是整个kmp中最核心的地方  
  7. int get_next(const char*t, int *next)  
  8. {  
  9.     int i = 0;  
  10.     int j = -1; //设置j = -1,非常巧妙  
  11.     int len = strlen(t);  
  12.     memset(next,0, sizeof(int) * len);  
  13.     next[0] = -1;  
  14.     while(i < len - 1)  
  15.     {  
  16.         if(j == -1 || t[i] == t[j]) //前面的判断,j == -1, 非常巧妙  
  17.         {  
  18.             i++;  
  19.             j++;  
  20.             next[i] = j;    //将后面的next数组元素赋值  
  21.         }  
  22.         else  
  23.             j = next[j];  
  24.     }  
  25. }  
  26.   
  27. int kmp(const char *s, const char *t)  
  28. {  
  29.     int i = 0;  
  30.     int j = 0;  
  31.     int next[100];  
  32.     get_next(t,next);  
  33.     while(i < strlen(s) && j < strlen(t))  
  34.     {  
  35.         if(j == - 1 || s[i] == t[j])    //如果j为-1,或者模式串和主串相等,两者继续往下比较  
  36.         {  
  37.             i++;  
  38.             j++;  
  39.         }  
  40.         else  
  41.             j = next[j];  
  42.     }  
  43.   
  44.     if(j >= (int)strlen(t))  
  45.     {  
  46.         cout << "found " << endl;  
  47.         return 0;  
  48.     }  
  49.   
  50.     cout << "not found" <<endl;  
  51.     return 0;  
  52. }  
  53.   
  54. //暴力法  
  55. int brute_force(const char *s, const char *t)  
  56. {  
  57.     int i, j;  
  58.     i = 0;  
  59.   
  60.     while(i < strlen(s))  
  61.     {  
  62.         j = 0;  
  63.         while(j < strlen(t))  
  64.         {  
  65.             if(s[i] == t[j])  
  66.             {  
  67.                 i++;  
  68.                 j++;  
  69.             }  
  70.             else  
  71.             {  
  72.                 i = i - j + 1;  
  73.                 break;  
  74.             }  
  75.         }  
  76.   
  77.         if(j == (int)strlen(t))  
  78.         {  
  79.             cout << "found" << endl;  
  80.             return 0;  
  81.         }  
  82.     }  
  83.   
  84.     cout << "not found" << endl;  
  85.     return 0;  
  86.   
  87. }  
  88.   
  89. int main()  
  90. {  
  91.     brute_force("abcdef""abcdef");  
  92.     kmp("abcdef""aaaa");  
  93.     return 0;  
  94.   
  95. }  

总结:

KMP算法非常经典,同时这个算法实现很多地方非常巧妙。


优化思路

KMP算法是可以被进一步优化的。
我们以一个例子来说明。譬如我们给的P字符串是“abcdaabcab”,经过KMP算法,应当得到“特征向量”如下表所示:
下标i
0
1
2
3
4
5
6
7
8
9
p(i)
a
b
c
d
a
a
b
c
a
b
next[i]
-1
0
0
0
0
1
1
2
3
1
但是,如果此时发现p(i) == p(k),那么应当将相应的next[i]的值更改为next[k]的值。经过优化后可以得到下面的表格:
下标i
0
1
2
3
4
5
6
7
8
9
p(i)
a
b
c
d
a
a
b
c
a
b
next[i]
-1
0
0
0
0
1
1
2
3
1
优化的next[i]
-1
0
0
0
-1
1
0
0
3
0
(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符
相同,且j的前面的1—k个字符与开头的1—k
个字符不等(或者相等但T[k]==T[j])(1≤k<j)。
如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6]
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个
字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]。。。T[k-1]==
T[j-k]T[j-k+1]T[j-k+2]…T[j-1]
且T[j] != T[k].(1≤k<j);
(4) next[j]=0 意义:除(1)(2)(3)的其他情况。

posted @ 2015-09-19 14:42  hust_liye  阅读(266)  评论(0编辑  收藏  举报