一、引入

  话说trie树是一切字符串上的高级数据结构的基础,那么KMP就是一切字符串匹配的基础。

  所以我们来说说如何看毛片KMP。

二、正题

  KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。

  虽然说这种问题用string库里的函数find()可以过,但KMP不仅仅是算法,更是一种很重要的思想。

  先看一个栗子:P=“abcabcdaaabcabcd”,T=“abcabcabc”,求T在P中的位置。

  1、匹配第一位

    abcabcdaaabcabcd

    abcabcabc

  2、匹配第二位

    abcabcdaaabcabcd

    abcabcabc

  3、匹配第三位

    abcabcdaaabcabcd

    abcabcabc

  。。。

  7、匹配第七位,失配

    abcabcdaaabcabcd

    abcabcabc

  怎么办呢?

  暴力:T串后移一位,重新开始(果然够暴力)

    abcabcdaaabcabcd

      abcabcabc

  可是已经匹配了六位了,舍不得,怎么办?

  仔细观察,哎!

    (abc)1(abc)2daaabcabcd

    (abc)3(abc)4abc

  我们发现(abc)4也是T串的前缀((abc)3),而且在P串中已经匹配了((abc)2)

  那么我们完全可以让(abc)3来和(abc)2匹配。

  KMP:

    abcabcdaaabcd

          abcabcabc

三、失配指针

  从上面描述的算法思想可以知道,KMP的关键在于失配之后T串怎么移动。

  我们引入一个概念:失配指针f[i]。它表示T串1~f[i]位于i-f[i]+1~i位是相同的,那么如果T串第i位和P串第j位失配了,那么就让T串第f[i]位和P串第j位匹配。

  通过失配指针我们很容易就能完成T串移动的任务。

  那失配指针怎么求呢?

  假设我们已知f[i-1],那么令j=f[i-1]。

    1、如果T[j+1]==T[i],则f[i]=j+1。

    2、如果T[j+1]!=T[i],则令j=f[j],递归下去。

  代码如下:

 1 void get_nxt(char *b)
 2 {
 3     int lenb=strlen(b+1);
 4     nxt[1]=0;
 5     for(int i=2,j=0;i<=lenb;i++)
 6     {
 7         while(j>0&&b[i]!=b[j+1])
 8             j=nxt[j];
 9         if(b[i]==b[j+1])
10             j++;
11         nxt[i]=j;
12     }
13 }//nxt数组即为文中的f数组(别喷我懒)

 

  再者就是匹配了。

  设此时T串匹配到i,P串匹配到j,则有:

    1、T[i+1]==p[j+1],则i++,j++。

    2、T[i+1]!=P[j+1],则令i=f[i],递归下去,直到T[i+1]==p[j+1]。

  代码如下:

 1 for(int i=1,j=0;i<=lena;i++)
 2 {
 3     while(j>0&&a[i]!=b[j+1])
 4         j=nxt[j];
 5     if(a[i]==b[j+1])
 6         j++;
 7     if(j==lenb)
 8     {
 9         printf("%d\n",i-lenb+1);
10         j=nxt[j];
11     }
12 }

 

 四、例题

  这里就以洛谷 P3375【模板】KMP字符串匹配 为例

  https://www.luogu.org/problemnew/show/P3375

  注意这题最后还要输出nxt数组。

  代码如下:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<vector>
 8 using namespace std;
 9 char a[1000010],b[1000010];
10 int nxt[1000010];
11 int main()
12 {
13     scanf("%s%s",a+1,b+1);
14     int lena=strlen(a+1),lenb=strlen(b+1);
15     nxt[1]=0;
16     for(int i=2,j=0;i<=lenb;i++)
17     {
18         while(j>0&&b[i]!=b[j+1])
19             j=nxt[j];
20         if(b[i]==b[j+1])
21             j++;
22         nxt[i]=j;
23     }
24     for(int i=1,j=0;i<=lena;i++)
25     {
26         while(j>0&&a[i]!=b[j+1])
27             j=nxt[j];
28         if(a[i]==b[j+1])
29             j++;
30         if(j==lenb)
31         {
32             printf("%d\n",i-lenb+1);
33             j=nxt[j];
34         }
35     }
36     for(int i=1;i<=lenb;i++)
37     {
38         if(i!=1)
39             printf(" ");
40         printf("%d",nxt[i]);
41     }
42     return 0;
43 }