zoukankan      html  css  js  c++  java
  • KMP算法

    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
    ",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
  • 相关阅读:
    search方法的使用
    边界字符的使用
    重复数量限定符
    常用匹配符
    使用JS快速读取TXT文件
    基于jq和纯js的 读取本地.txt文件的方法
    Linux中的du和df命令
    HSSFWorkbook
    el表达式
    eclipse 导入web项目时常见错误
  • 原文地址:https://www.cnblogs.com/wxyww/p/9332446.html
Copyright © 2011-2022 走看看