第一篇:从经典的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

posted on 2012-10-28 04:46  MrWrong77  阅读(387)  评论(0编辑  收藏  举报

导航