• KMP


    洛谷P3375:https://www.luogu.com.cn/problem/P3375

    Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个词W的出现位置。

    KMP算法的作用是高效解决字符串匹配问题;

    一般用暴力的方法解决字符串匹配问题是这样的

     这个例子里我们可以看出第一次匹配失败第二次可以右移4位,而不是暴力算法的一位。也就是说这些步骤是没有意义的。我们想要优化就可以从这里入手。

    我们可以判断出右移4位是因为前面的AB可以直接移到后面的AB处,这里就可以引进前缀后缀。仔细想一想,我们要移动的位数就是现在重合部分减去最长公共前后缀。

    有了思想之后就要想怎么用代码实现了。

    我们先要用nextt数组记录模板字符串从第一位到第i位的最长公共前后缀的长度。

    如何记录呢,代码如下

     重点注意i从2开始,nextt[1]=0。为什么呢,读者可以试一试从1开始然后输出nextt数组。

    然后这段代码最重要的部分就是while那一行了。这也是比较难理解的地方。

    每次匹配i和j+1.如果j+1不匹配,就跳到next[j]。第一次跳,匹配next[j]+1,再跳,就匹配next[next[j]] + 1.一直到j=0或者匹配成功。

    此过程相当于模式串的自我匹配,所以不断的递归j = nextt[j],直到要么找到长度更短的相同前缀后缀,要么没有长度更短的相同前缀后缀

    本人描述水平不佳,建议画图理解。

    找到nextt数组后再进行比较。

     while的过程和上面求nextt数组一样。

    if(j==s2_l)是匹配的情况,后面可以进行一些操作,根据题目而定。此处是记录在文本字符串中匹配的位置。

    其实我们可以发现这两个主要的过程差不多。

    KMP算法就是对模板字符串自己进行一次匹配操作,然后再对文本字符串进行一次匹配操作。

    完整代码如下

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    using namespace std;
    char s1[1000001],s2[1000001];
    int nextt[1000001],s1_l,s2_l,ans[1000001],t=0;
    void KMP(char *str)
    {
     memset(nextt,0,sizeof(nextt));
     int j=0;
     for(int i=2;i<=s2_l;i++)
     {
      while(j&&str[j+1]!=str[i]) j=nextt[j];
      if(str[j+1]==str[i]) j++;
      nextt[i]=j;
     }
    }
    int main()
    {
     //cin>>s1;
     scanf("%s%s",s1+1,s2+1);
     s1_l=strlen(s1+1);s2_l=strlen(s2+1);
     KMP(s2);
     int j=0;
     for(int i=1;i<=s1_l;i++)
     {
      while(j&&s2[j+1]!=s1[i]) j=nextt[j];
      if(s2[j+1]==s1[i]) j++;
      if(j==s2_l)
      {
       ans[++t]=i-s2_l+1;
       }
     }
     for(int i=1;i<=t;i++)
      printf("%d ",ans[i]);
     for(int i=1;i<=s2_l;i++) printf("%d ",nextt[i]);
     return 0;
     }
     
  • 相关阅读:
    第三次作业
    第二次作业
    第一次作业
    实验二
    第一次试验
    第五次作业
    第四次作业
    第三次作业
    第二次作业
    第一次作业
  • 原文地址:https://www.cnblogs.com/xcsj/p/12215589.html
Copyright © 2020-2023  润新知