kmp算法详解

转自:http://blog.csdn.net/ddupd/article/details/19899263

 

 

KMP算法详解 

KMP算法简介:

         KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简单的就是BF算法。BF算法是用两个游标分别指向母串S,模式串T,从开头向后面依次比较字符是否相等,如果相等继续同时向后滑动两个游标,不相等的话,T的游标回溯至开头,S的游标回溯至起初游标的下一位,这种算法原理非常简单,小学生都可以想的到。

         KMP算法是在BF算法的基础上加以改进的,它的特点是在遇到字符不匹配时候维持母串T的游标不动,而把模式串向右移动,具体移动到哪一个元素下标,这就是算法的核心思想之处了。

         假如母串的i处和模式串的j处不匹配,那么就令k=next(j),表示的意思就是:模式串在j处出现不匹配现象,此时应该将模式串向后一定到下标为k的游标处,在此与之前不匹配的元素进行比较。

Kmp算法的本质:

         如图所示:

 

在下标j处出现不匹配,则k = next(j),表示此时应该把下标k移动到原本j对应的位置处,用T[k]跟s[i]进行对比。如果满足这样的条件,则有T[0],T[1],…T[k-1] = S[i-k],S[i-k+1],…S[i-1]

又因为j之前的字符串跟S都匹配,所以又有T[j-k],T[j-k+1],…T[j-1] = S[i-k],S[i-k+1],…S[i-1].所以得出  T[0],T[1],…T[k-1] = T[j-k],T[j-k+1],…T[j-1]。也就是说图中被标记出来前后两个区域的字符串相等,KMP算法的本质就是找出最大的这样一个k值满足T[0],T[1],…T[k-1] = T[j-k],T[j-k+1],…T[j-1]。

K值的求取方法:

K值的求取用到了数学中的递推的思想,求取K值只跟模式串T自身有关,跟母串S半毛钱关系都没有。先假设已经有 next(j) = k,接下来我们就去求next(j+1)的值。这个要分情况讨论:

如果T[k] = T[j]那么就很容易得到 next(j+1) = k+1 = next(j) + 1;

如果T[k] != T[j],那么此时可以将T[0],T[1],…T[k-1],T[k]看做一个模式串,T[j-k],T[j-k+1],…T[j-1],T[j]看做一个母串,此时模式串在k处出现不匹配现象,那么我们获取next(k)= k1的值,判断T[k1]跟T[j]的值是否相等,如果相等那么next(j+1) = k1+1;如果不相等再往下求新的kn的值,直到T[kn]= T[j],或者遍历到了模式串的开头都不想的话,此时就要把i向后移动一个位置,同时用模式串的开头指向i,或者抽象一点就是把模式串开头的前一位下标(-1)指向i。因为下标(-1)是没有意义的,所以此时等效于下标(0)指向母串的i+1。

算法的实现:

这里一共列出了三个版本的kmp算法,其中第一个是本人根据对算法的理解写的,也是最丑的一个,剩下的两个是改编严蔚敏版的《数据结构与算法》一书中的。

 

 1 //Algorithms.h
 2 #pragma once
 3 #include <string>
 4 using namespace std;
 5 class Algorithms
 6 {
 7 public:
 8     Algorithms(void);
 9     static int kmp1(string str1,string str2);
10     static int kmp2(string str1,string str2);
11     static int kmp3(string str1,string str2);
12     ~Algorithms(void);
13 };

 

 

 

  1 //Algorithms.cpp
  2 #include "Algorithms.h"
  3 #include <vector>
  4 #include <iostream>
  5 using namespace std;
  6 
  7 Algorithms::Algorithms(void)
  8 {
  9 }
 10 Algorithms::~Algorithms(void)
 11 {
 12 }
 13 
 14 int Algorithms::kmp1(string strS,string strT)
 15 {
 16     int nSize = strT.size();
 17     vector<int> vecNext(nSize,-1);
 18     if (nSize >= 2)
 19         vecNext[1] =0;
 20     for (int i=2;i<nSize;i++)
 21     {
 22         int k = vecNext[i-1];
 23         while (k>=0 && strT[k] != strT[i-1] )
 24             k = vecNext[k];
 25         if(k>=0 && strT[i-1] == strT[k])
 26             vecNext[i] = k + 1;
 27         else
 28             vecNext[i] = 0;
 29     }
 30     for(int i=0;i<nSize;i++)
 31         cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl;
 32 
 33     int nLength = strS.size();
 34     int nCount = 0;
 35     int nPoss = 0;
 36     int nPost = 0;
 37     
 38     while(nPoss < nLength)
 39     {
 40         if ( strS[nPoss] == strT[nPost] )
 41         {
 42             nPoss++;
 43             nPost++;
 44         }
 45         else
 46         {
 47             nPost = vecNext[nPost];
 48             if (nPost == -1)
 49             {
 50                 nPoss++;
 51                 nPost++;
 52             }
 53         }
 54 
 55         if (nPost == nSize )
 56         {
 57             nCount++;
 58             nPost = 0;
 59         }
 60     }
 61     return nCount;
 62 }
 63 
 64 int Algorithms::kmp2(string strS,string strT)
 65 {
 66     int nSize = strT.size();
 67     vector<int> vecNext(nSize);
 68     int i = 0;
 69     vecNext[0] = -1;
 70     int j = -1;
 71     while(i<nSize-1)
 72     {
 73         if (j==-1 || strT[i]==strT[j])
 74         {
 75             i++;
 76             j++;
 77             vecNext[i] = j;
 78         }
 79         else
 80             j = vecNext[j];
 81     }
 82     for(int i=0;i<nSize;i++)
 83         cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl;
 84 
 85     int nLength = strS.size();
 86     int nCount = 0;
 87     int nPoss = 0;
 88     int nPost = 0;
 89     
 90     while(nPoss < nLength)
 91     {
 92         if ( strS[nPoss] == strT[nPost] )
 93         {
 94             nPoss++;
 95             nPost++;
 96         }
 97         else
 98         {
 99             nPost = vecNext[nPost];
100             if (nPost == -1)
101             {
102                 nPoss++;
103                 nPost++;
104             }
105         }
106 
107         if (nPost == nSize )
108         {
109             nCount++;
110             nPost = 0;
111         }
112     }
113     return nCount;
114 }
115 
116 int Algorithms::kmp3(string strS,string strT)
117 {
118     int nSize = strT.size();
119     vector<int> vecNext(nSize);
120     int i = 0;
121     vecNext[0] = -1;
122     int j = -1;
123     while(i<nSize-1)
124     {
125         if (j==-1 || strT[i]==strT[j])
126         {
127             i++;
128             j++;
129             if (strT[i] == strT[j])
130                 vecNext[i] =vecNext[j];
131             else
132                 vecNext[i] = j;
133         }
134         else
135             j = vecNext[j];
136     }
137     for(int i=0;i<nSize;i++)
138         cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl;
139 
140     int nLength = strS.size();
141     int nCount = 0;
142     int nPoss = 0;
143     int nPost = 0;
144     
145     while(nPoss < nLength)
146     {
147         if ( strS[nPoss] == strT[nPost] )
148         {
149             nPoss++;
150             nPost++;
151         }
152         else
153         {
154             nPost = vecNext[nPost];
155             if (nPost == -1)
156             {
157                 nPoss++;
158                 nPost++;
159             }
160         }
161 
162         if (nPost == nSize )
163         {
164             nCount++;
165             nPost = 0;
166         }
167     }
168     return nCount;
169 }

 

 

 1 //main.cpp
 2 #include <iostream>
 3 #include <string>
 4 #include <vector>
 5 #include "Algorithms.h"
 6 using namespace std;
 7 
 8 void main()
 9 {
10     string str1,str2;
11     cout<<"please input str1:"<<endl;
12     cin>>str1;
13     cout<<"please input str2:"<<endl;
14     cin>>str2;
15     //cout<<"the number of substr in str1 is:"<<Algorithms::kmp1(str1,str2)<<endl;
16     //cout<<"the number of substr in str1 is:"<<Algorithms::kmp2(str1,str2)<<endl;
17     cout<<"the number of substr in str1 is:"<<Algorithms::kmp3(str1,str2)<<endl;
18     system("pause");
19 }

 

 

算法评析:

1:Kmp1

    先说下我自己写的吧,代码没有书上的简洁,再说说几个为什么。为什么循环要从i=2开始?

因为要用到int k = vecNext[i-1],以及strT[k] != strT[i-1],如果从i=1开始的话,k的起始值=-1,这样就会出现越界的情况,所以就从i=2开始;另外

 next(0)=-1,next(1)=0,这都是毫无疑问的东西,所以可以在前两者已知的情况下,向后求解。

2:Kmp2

    Kmp2的巧妙之处在于用了while循环,在需要i变化时才变化,否则已知变换j的值(j表示的是next(i))

3:Kmp3

    Kmp3是对kmp2的改进,它考虑到了这样的一种情况:首先在T(i) = T(j)的情况下,如果按照kmp2那么next(i+1)= j+1。但是如果T(i+1)=T(j+1)的话,那么此时就无需在拿T(j+1) 跟母串的比较,因为T(i+1)已经比较过了,并且他们不相等,所以不需要再比较T(j+1),

只需要令:       next(i+1) = next(j+1)。

按照kmp2其实是这样的:  next(i+1) = j+1,j+1 = next(j+1),

所以中间省略的一步就是:next(i+1)= j+1。

总结:

关于kmp网上的讲解很多,但是坑爹的错误程序也不少,在验证一个kmp算法程序是否坑爹,只需要把next数组的内容打印出来,将它和自己口算得到的结果比对。百度百科的C++实现的kmp算法就是错误的,坑爹的。

posted @ 2014-08-15 09:35  明明是悟空  阅读(1294)  评论(0编辑  收藏  举报