KMP算法???

  kmp算法最简单的就是用来匹配子串,也就是从字符串s1中找到s2出现的次数与位置。同时,kmp的nxt数组还有很多高能的用处。

NXT数组:

  nxt数组是kmp算法中极其重要的部分,nxt[i]表示子串s中,上一次s[i]为后缀的位置。(看了后面的原理就很明白了)

大致原理:

  举个例子:假如母串s1为abcabcaba  子串s2为abcaba

    如果进行暴力匹配的话,就是先从母串第一个字母开始匹配,s1[1]=s2[1],继续找s1[2]=s2[2],继续,s1[3]=s2[3]......s1[5]=s2[5],然后s1[6]!=s2[6]!!!!!重新开始,s1[2]!=s2[1].........这就使复杂度瞬间增高了,很容易被卡成O(len(s1)*len(s2))

  然后来看kmp如何处理。先找出s2的nxt数组,上面这个样例中nxt[1]=0,nxt[2]=0,nxt[3]=0,nxt[4]=1,nxt[5]=2,nxt[6]=4。

  然后按照与暴力相同的方法往后匹配。当匹配到s1[5]时,发现下一个就不匹配了,先别着急从新开始,这是就用到了nxt数组,虽然下一个不匹配的,但是不代表前面的努力都白费了。至少说明s1的当前位置与s2的当前位置是匹配的。然后我们可以从s2[2]开始继续往下与s1的s1[5]匹配,观察下一个元素是否匹配,这时发现s1[6]与s2[3]是匹配的,继续往下匹配,最后发现完全匹配。任务完成。

  两者比较而言,kmp减少了每次匹配不成功时重新匹配的麻烦,而是借助于先前的努力继续匹配,这样就大大节省了时间。这一优点在s2的元素变得更多时,体现的更明显。

大致过程:

  kmp算法的大致过程,分为两步。

  第一步:

    子串的自身匹配。也就是找nxt数组,找法与kmp的匹配原理类似。

    还是上面那个例子:abcaba

      假如说当前处理到了nxt[3](也就是从1到2的nxt都处理好了),因为在s[3]之前没有'c',所以nxt[3]=0,然后去处理s[3]的下一位s[4],s[4]与nxt[3]的下一位正好匹配,所以nxt[4]=nxt[3]+1=1,然后匹配s[5],发现s[5]与nxt[4]的下一位正好匹配,所以nxt[5]=nxt[4]+1=2然后是s[6],发现nxt[5]的下一位与s[6]不同。所以去找nxt[5]的nxt,也就是0,然后发现0的下一位与s[6]匹配,所以nxt[6]=0+1=1。这里为什么nxt[6]不是4??因为不能保证从s[1]到s[4]与从s[3]到s[6]相同,如果这里写成4的话,按照上面的原理进行匹配,很明显会匹配出错误来,模拟一下就很明白了。

  第二步:

    母串与子串的匹配。按照上面的原理进行操作即可,具体就是用一个模拟指针p来指向当前匹配到的子串中的哪一位置,当p指向了子串的末端,那么说明完成了一个匹配。这是要将指针指向当前指针的nxt。

 一道板子题luogu3375

  代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 const int N=1000010;
 6 int nxt[N],f[N],p;
 7 int l1,l2;
 8 char s1[N],s2[N];
 9 void get_nxt()
10 {
11     p=0;nxt[1]=0;
12     for(int i=2;i<=l2;++i)
13     {
14         while(p>0&&s2[i]!=s2[p+1]) p=nxt[p];
15         if(s2[i]==s2[p+1]) p++;
16         nxt[i]=p;
17     }
18 }
19 void kmp()
20 {
21     p=0;int ans=0;
22     for(int i=1;i<=l1;++i)
23     {
24         while(p>0&&s1[i]!=s2[p+1]) p=nxt[p];
25         if(s1[i]==s2[p+1]) p++;
26         if(p==l2) 
27         {
28             ans++;
29             p=nxt[p];
30             printf("%d\n",i-l2+1);
31         }
32     }
33     return ;
34 }
35 int main()
36 {
37    scanf("%s%s",s1+1,s2+1);
38    l1=strlen(s1+1);
39    l2=strlen(s2+1);
40    get_nxt();
41    kmp();
42    for(int i=1;i<=l2;++i)
43         printf("%d ",nxt[i]);
44     return 0;
45 }
luogu3375