第一篇:从经典的KMP算法开始
算法不好不要紧,关键有一颗爱学习的心。
最近因为找工作的原因,花了较多的时间准备算法和数据结构,各种学习和练习。然后至今未拿到offer,遇到了极大的瓶颈,感觉看书都快看不下去了。
可是打开DevCpp,觉得这两个月虽然没有收获offer,但是在算法和数据结构方面却收获了很多。
对于本科非计算机专业、未参加过ACM类竞赛且研究生期间实验室的研究工作基本不接触算法的人来说,还是十分享受这个学习的过程。
在就业的瓶颈期,开始考虑将自己写的大量练习代码搬到博客里,一方面激励自己继续学习,一方面可以给各位网友一个参考。
我的大部分代码都经过自己反复验证和推敲,但无法保证没有问题。如发现Bug还请一起讨论,谢谢支持。
好了言归正传,关于KMP算法。此处只讲精髓。
简单的字符串匹配算法,众做周知,其最坏时间复杂度是O(M*N),其中M为目标串char *s的长度,N为模式串char *p的长度。
显然,实际匹配所需操作数一般远低于该上限,只有对于char s[] = "aaaaaaaaaab",char p[] = "aaaaaab"这样比较奇葩的组合,才会逼近O(M*N)的复杂度。
不过三位前辈(他们三位各自名字的第一个字母组成KMP)发明了KMP算法,终结了我们对于简单字符串匹配算法实际有多慢的讨论,这一算法的时间复杂度是惊人的O(M+N)。
KMP算法的精髓在于,对于模式串,生成一个NEXT数组,以借助上一次匹配的信息,优化匹配步骤,避免一些重复的比较。优化的核心思想是为了避免在目标串中的“回头”。想象一下,我们有2个指针,一个叫char *ps,指向当前比较的目标串中的字符;一个叫char *pp,指向当前比较的模式串中的字符。NEXT数组的存在,就是避免ps回头的操作,在该匹配算法中,ps只有ps++,一路向右不回头。
NEXT数组还有很多其他叫法,如OVERLAP数组等等,详见各类关于KMP算法的讲解资料。
NEXT数组的生成代码如下:
1 int *MakeNextArr(char *p) 2 { 3 int len = strlen(p); 4 if(len < 1) 5 return NULL; 6 int *nextArr = new int[len]; 7 if(NULL == nextArr) 8 return NULL; 9 10 //生成NEXT数组 11 int k = 0, i = 1; 12 nextArr[0] = 0; 13 while(i < len) 14 { 15 while(k > 0 && p[i] != p[k]) 16 k = nextArr[k]; 17 if(p[i] == p[k]) 18 k++; 19 nextArr[i++] = k; 20 } 21 22 //打印出模式字符串和对应NEXT数组 23 cout<<"Pattern String: "<<p<<endl; 24 cout<<"Next Array: "; 25 for(int i = 0; i < len; ++i) 26 cout<<nextArr[i]<<" "; 27 cout<<endl; 28 29 return nextArr; 30 }
KMP匹配的代码如下:
1 int KMPSearch(char *s, char *p) 2 { 3 int lenS = strlen(s); 4 int lenP = strlen(p); 5 if(0 == lenS || 0 == lenP) 6 return -1; 7 8 //获取NEXT数组 9 int *nextArr = MakeNextArr(p); 10 if(NULL == nextArr) 11 return -1; 12 13 //进行KMP字符串匹配 14 int pp = 0, ps = 0, pos = -1; 15 while(ps < lenS) 16 { 17 while(pp > 0 && p[pp] != s[ps]) 18 pp = nextArr[pp]; 19 if(p[pp] == s[ps]) 20 pp++; 21 if(pp == lenP) 22 { 23 pos = ps - lenP + 1; 24 break; 25 } 26 ps++; 27 } 28 29 delete [] nextArr; 30 return pos; 31 }
完整代码如下:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 int * MakeNextArr(char *p) 7 { 8 int len = strlen(p); 9 if(len < 1) 10 return NULL; 11 int *nextArr = new int[len]; 12 if(NULL == nextArr) 13 return NULL; 14 15 //生成NEXT数组 16 int k = 0, i = 1; 17 nextArr[0] = 0; 18 while(i < len) 19 { 20 while(k > 0 && p[i] != p[k]) 21 k = nextArr[k]; 22 if(p[i] == p[k]) 23 k++; 24 nextArr[i++] = k; 25 } 26 //打印出模式字符串和对应NEXT数组 27 cout<<"Pattern String: "<<p<<endl; 28 cout<<"Next Array: "; 29 for(int i = 0; i < len; ++i) 30 cout<<nextArr[i]<<" "; 31 cout<<endl; 32 33 return nextArr; 34 } 35 36 int KMPSearch(char *s, char *p) 37 { 38 int lenS = strlen(s); 39 int lenP = strlen(p); 40 if(0 == lenS || 0 == lenP) 41 return -1; 42 43 //获取NEXT数组 44 int *nextArr = MakeNextArr(p); 45 if(NULL == nextArr) 46 return -1; 47 48 //进行KMP字符串匹配 49 int pp = 0, ps = 0, pos = -1; 50 while(ps < lenS) 51 { 52 while(pp > 0 && p[pp] != s[ps]) 53 pp = nextArr[pp]; 54 if(p[pp] == s[ps]) 55 pp++; 56 if(pp == lenP) 57 { 58 pos = ps - lenP + 1; 59 break; 60 } 61 ps++; 62 } 63 64 delete [] nextArr; 65 return pos; 66 } 67 68 int main() 69 { 70 char s[] = "abababcabababd"; 71 char p[] = "abababd"; 72 cout<<"Position of \""<<p<<"\" in \""<<s<<"\" is "<<KMPSearch(s, p)<<endl; 73 74 return 0; 75 }
程序输出:
Pattern String: abababd
Next Array: 0 0 1 2 3 4 0
Position of "abababd" in "abababcabababd" is 7
转载请注明:来自觅斯特Wrong的博客:http://www.cnblogs.com/fkwang/archive/2012/10/28/2743111.html