• KMP


    KMP#

    问题模式##

    给定模式串s1,目标串s2,问s1出现在s2的次数以及位置

    期望复杂度:O(n+m)

    算法部分##

    常规而言,如果之间去扫描目标串并check是否匹配,复杂度就会达到O(n*m)

    很自然为了降低复杂度,我们需要更好的方式

    比如当aaa匹配成功,我们先不看后面的情况,单论aaa本身:

    模式串:aaa

    欲匹配的目标串: aaa张三李四王五...

    目标串1: aaa张三李四王五...

    目标串2: aa张三李四王五...

    目标串3: a张三李四王五...

    如果aaa能匹配,目标串1中前3个a必能匹配,目标串1中前2个a必能匹配,目标串1中前1个a必能匹配,而目标串2,3是由目标串1后移的结果

    所以,如果模式串某个位置能够匹配成功,可能会存在前j个元素在后移过程中也能够完全匹配

    我们现在需要记录next[i]=j,他的含义是:

    如果模式串中位置i匹配成功,一定存在模式串前j个元素能够完全匹配

    (这里位置i是字符串下标,从0开始计数,j是个数,可能为0,注意区分)

    也称为位置i之前的字符串中最长相同前后缀的长度为j

    例如对于aaabaaaai,有:

    模式串:aaabaaaai

    对应的:012012330

    失配处理:我们如果有了next数组,那我们在i失配的时候就可以一定可以说前j个元素一定是匹配的,现在只需要check模式串第j号下标与当前目标串位置元素的关系,不匹配就继续进行当前失配处理。

    KMP的精髓之处在于如何找这个next数组:

    其实这个过程运用的就是上面一样的过程,一言以蔽之,“自己匹配自己”

    现在只不过将原来的模式串当作现在的目标串来匹配,使用的是一样的过程,

    对于位置i而言,next[i]就是他当前能匹配成功的个数

    还有一点,匹配成功了怎么办?

    判断成功,触发反馈,再当作失配来处理,

    这两块匹配过程可以拆开写,也可以合起来写,下面展示的核心代码是合起来的。

    核心代码##

    s1是目标串,s2是模式串

    一定要区分好下标与个数的差别,很容易出错!!

    s1=s2+"#"+s1;//连接处理,让两个过程合并,记得加上#,否则一些特例会直接暴毙
    next[0]=0;
    for(int i=1;i<s1.size();i++)   //这里指的是字符串下标的遍历,从标号1开始
    {
    	 //模式串中第0号到第c-1号均已经匹配,匹配个数是c个,开始检测第c号下标
    	 while(c!=0&&s1[c]!=s1[i])
         c=next[c-1];
    	if(s1[c]==s1[i])c++;//if(1)那么说明第c号下标匹配成功,那么有c+1个数已经匹配成功
    	 if(c==len&&i>len)       //匹配成功了继续当作失配处理
         {
            cout<<i-len*2+1<<endl;
            c=next[c-1];
         }
         next[i]=c;					//c指的是当前匹配成功的个数
     }
    

    洛谷P3375模版##

    #include<iostream>
    #include<cstring>
    #include<iostream>
    #include<string>
    using namespace std;
    #define INF 1e10+5
    #define maxn 10005
    #define minn -105
    #define ld long double;
    #define uint unsigned int;
    #define ull unsigned long long;
    typedef long long ll;
    int main()
    {
        string s1,s2;
        int c;
        int next[maxn<<1];
        cin>>s1;
        cin>>s2;
        int len=s2.size();
        s1=s2+"#"+s1;
        next[0]=0;
        for(int i=1;i<s1.size();i++)
        {
            while(c!=0&&s1[c]!=s1[i])
                c=next[c-1];
            if(s1[c]==s1[i])c++;
            if(c==len&&i>len)
            {
                cout<<i-len*2+1<<endl;
    	            c=next[c-1];
            }
            next[i]=c;
        }
        for(int i=0;i<len;i++)
            cout<<next[i]<<" ";
        return 0;
    }
    

    CF Global Round 7 D2##

    核心问题是求前缀最长回文串

    注意所求内容是具有前缀性的,所以它模式串是确定的,只不过模式串的长度不定

    而将回文串逆置,则根据回文串的性质,这个就是目标串

    AC:https://www.luogu.com.cn/record/31985959

  • 相关阅读:
    聊聊简单又灵活的权限设计(RBAC)
    手把手搭建一个属于自己的在线 IDE
    聊一聊如何搭建高性能网站哪一些事
    一个老程序员的忠告:你这辈子输就输在以为靠技术就能生存下
    缓存提升性能的关键性手段
    python学习笔记1之-python简介及其环境安装
    聊一聊mycat数据库集群系列之双主双重实现
    mycat数据库集群系列之mycat读写分离安装配置
    mycat数据库集群系列之mysql主从同步设置
    mycat数据库集群系列之数据库多实例安装
  • 原文地址:https://www.cnblogs.com/et3-tsy/p/12545387.html
Copyright © 2020-2023  润新知