终结字符串匹配----------KMP算法

 

对于KMP很早之前就学过,但是一直没写博客,当再次用到时,发现忘的差不多了,所以题主花了两个小时重新拾取了一下,现在打算留下笔记,写下自己对KMP算法的理解。(在看本文章之前需要对KMP大致的理解,本文章只针对Next数组的求法+代码解析)

 

第一个问题:

  字符串1:  abacabababacababc

  字符串2:  abacababc

    

    在字符串1中查找字符串2出现的次数,常规思路,暴力,时间复杂度:O(n2),只能解决1000长度以内的字符串。

 

暴力算法因为存在指针回溯,导致复杂度偏高。暴力基础上解决指针回溯就是KMP算法,时间复杂度:O(n)

 

学习KMP算法前,需要先学习下什么是最长公共前后缀。

 

假设字符串长度为len,    即str【0】 ~  str【len-1】  , 设定 K在区间 【0,len-1】内的任意整数

字符串前缀:包含首字符在内的子串。         即  子串:  str【0】 ~ str【k】

字符串后缀:包含尾字符在内的子串。    即  子串:  str【k】 ~ str【len-1】

公共前后缀:即字符串前缀完全等于字符串后缀。

最长公共前后缀:   公共前后缀加个最长条件即可。

 

那么来求字符串abacababc 的next。

 

第一步分离出全部的字符串前缀。

 

 

第二步:将得到最长公共前后缀长度表向下移一位,末尾舍去,首位填-1,得到的数组就是next数组。(如下图)

 

 

 

那么得到了next的数组有什么用呢?我们模拟一遍匹配就好了

 

还是上面的例子:

  字符串1:  abacabababacababc

  字符串2:  abacababc

我们得到的字符串2的next数组为 {-1,0,0,1,0,1,2,3,2};

 

 

首先暴力匹配, 设定变量i是指向字符串1待匹配的元素,  变量k指向字符串2待匹配元素

回退如下

 

 

 

 

 

 

 

 

 

 

不难看出:

    当不成功匹配的时,则k回退,k值等于对应的next的值。

  即:next数组的作用:  当发现不匹配时,k回退到对应的next值,即next值之前的元素都已经记忆匹配,如上图绿色下划线部分。

 

 

代码解析

 

 

 

 next:

 1 void GetNext(){   //next数组求法 
 2     int i=0,k=-1;
 3     Next[0]=-1;
 4     while (i<m){
 5         if (k==-1||st[i]==st[k]){
 6             i++;
 7             k++;
 8             Next[i]=k;
 9         }
10         else
11             k=Next[k];
12     }
13 }

 

唯一不好理解的应该是 第11行的 k=Next【k】,为什么要这样回退?

如下例子。

 

 

 

KMP:

 1 int KMP(char str[],char st[]){ //不存在输出-1,存在则输出首地址 
 2     GetNext();
 3     int i=0,j=0,n=strlen(str),m=strlen(st);
 4     while (i<n&&j<m){
 5         if (j==-1||str[i]==st[j]){
 6             i++;
 7             j++;
 8             
 9         }
10         else
11             j=Next[j];
12     }
13     if (j>=m)    return i-j+1;
14     return -1;
15 }

 

 

 

 

 附上签到题:

hdu1711

 1 #include<iostream>
 2 using namespace std;
 3 
 4 
 5 int n,m,str[1000010],st[1010],Next[10010];
 6 
 7 void GetNext(){
 8     int i=0,k=-1;
 9     Next[0]=-1;
10     while (i<m){
11         if (k==-1||st[i]==st[k]){
12             i++;
13             k++;
14             Next[i]=k;
15         }
16         else
17             k=Next[k];
18     }
19 }
20 
21 int KMP(){  
22     GetNext();
23     int i=0,j=0;
24     while (i<n&&j<m){
25         if (j==-1||str[i]==st[j]){
26             i++;
27             j++;
28             
29         }
30         else
31             j=Next[j];
32     }
33     if (j>=m)    return i-j+1;
34     return -1;
35 }
36 int main(){
37     int T;
38     scanf("%d",&T);
39     while (T--){
40         scanf("%d%d",&n,&m);
41         for (int i=0;i<n;i++)
42             scanf("%d",&str[i]);
43         for (int i=0;i<m;i++)
44             scanf("%d",&st[i]);
45         cout<<KMP()<<endl;
46     }
47     return 0;
48 }

 

 

hdu2087

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 char str[1010],st[1010];
 6 int n,m,ans,Next[1010];
 7 void GetNext(){
 8     int i=0,k=-1;
 9     Next[0]=-1;
10     while (i<m){
11         if (k==-1||st[i]==st[k]){
12             i++;
13             k++;
14             Next[i]=k;
15         }
16         else k=Next[k];
17     }
18 }
19 
20 void KMP(){
21     GetNext();
22     ans=0;
23     int i=0,j=0;
24     while (i<n){
25         if (j==-1||str[i]==st[j]){
26             i++;
27             j++;
28         }
29         else
30             j=Next[j];
31         if (j==m){
32             ans++;
33             j=0;
34         }
35     }
36     return ;
37 }
38 int main(){
39     while (true){
40         scanf("%s",str);
41         if (str[0]=='#')    break;
42         scanf("%s",st);
43         n=strlen(str);
44         m=strlen(st);
45         KMP();
46         printf("%d\n",ans);
47     }
48 
49     return 0;
50 }

 

posted @ 2020-11-22 13:31  生活待我如初恋  阅读(136)  评论(0编辑  收藏  举报