• 【字符串】拓展KMP


    【求模式串与主串的每一个后缀的最长公共前缀】

    有两个字符串 主串S 与 模式串T

    求主串S从第 i 个位置开始,与T的最长相同前缀的长度

    即 S[ i ]~S[ i+mlen ] = T[ 0 ]~T[ mlen ] 的 mlen 值

    对于 i∈[ 0,Slen ) 的任意一个i都求一遍

     

    定义:(这里的Next与KMP的Next数组意义不同)

     

    Next数组储存模式串T的第 i 个位置开始与自己(T)的最长相同前缀的长度

     

    Extend数组储存主串S的第 i 个位置开始与模式串T的最长相同前缀的长度

    首先假设 a 和 p 两个遍历,表示S串从 a 位置开始到 p-1 位置结束,与T串从 0 开始到 p-a 位置结束,这两段子串相匹配

    那么,如果现在处理到了第i个位置

    求的Extend[ i ]是S串第i个位置开始与模式串T的最长相同前缀的长度

    从这里开始分下面三种情况考虑

    讨论 i+Next[i-a] 与 p 的关系

    ①    如果 i+Next[i-a]<p,即此时三段字符串相同的情况如下

      显然,此时满足 Extend[ i ] = Next[ i-a ]

    ②    如果 i+Next[i-a]==p

      由刚才的定义可知 S[p]!=T[p-a] ,且满足该等式时说明 T[p-a]!=T[p-i]

      但是 S[p] 与 T[p-i] 的关系未知(如果相等,说明 Extend[i] 还能增大)

      所以此时就可以直接从 S[p] 和 T[p-i] 开始向后继续匹配(提高速度)

    ③    如果i+Next[i-a]>p

      因为 S[p]!=T[p-a] ,但是 T[p-a]==T[p-i] ,所以 S[p]!=T[p-i] 成立,所以 Next[i] 的值无意义,Extend[i] 的值就可以直接等于 p-i

    以上三种情况考虑完后就可以求出 Extend 数组了

    对于 Next 数组,因为求的是从 i 位置开始与自己的最长相同前缀的长度

    所以可以直接根据上述求Extend数组的思想求出Next数

    完整程序如下:

    #include<bits/stdc++.h>
    using namespace std;
    string S,T;
    int Slen,Tlen,Next[1000050],Extend[1000050];
    
    void getNext()
    {
        int i,a=0,p=0;
        Next[0]=Tlen;//特殊预处理
        for(i=1;i<Tlen;i++)
        {
            if(i>=p||i+Next[i-a]>=p)//如果i比目前处理到的p的值要大(此时要右移模式串然后重新寻找匹配的p,KMP),或者出现了第2、3种情况
            {
                if(i>p)
                    p=i;//如果i比p大,可以直接更新p
                while(p<Tlen&&T[p]==T[p-i])//尝试向右更新p值
                    p++;
                Next[i]=p-i;
                a=i;//右移模式串头部
            }
            else//第1种情况,直接拿i-a位置的值当答案
                Next[i]=Next[i-a];
        }
    }// Next[i] 表示 T 串内从 i 位置开始与 T 串前缀相同的最长长度
    
    void getExtend()//注释同上
    {
        int i,a=0,p=0;
        for(i=0;i<Slen;i++)
        {
            if(i>=p||i+Next[i-a]>=p)
            {
                if(i>p)
                    p=i;
                while(p<Slen&&p-i<Tlen&&S[p]==T[p-i])
                    p++;
                Extend[i]=p-i;
                a=i;
            }
            else
                Extend[i]=Next[i-a];
        }
    }// Extend[i] 表示 S 串内从 i 位置开始与 T 串前缀相同的最长长度
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        
        cin>>S>>T;
        Slen=S.size();
        Tlen=T.size();
        getNext();
        getExtend();
        
        
        return 0;
    }

    本文参考了https://subetter.com/algorithm/extended-kmp-algorithm.html,原文写得很好,图片也引用自原文

    例题 HDU 6629

    求的是按照最笨的办法去求字符串S除自己以外的每一个后缀与自己的最长公共前缀长度总和

    过程中进行两字符对比的次数

    只要把S也当作模式串T直接代入拓展KMP

    然后求除了 Extend[0] 外的所有Extend之和

    每次还要判断,如果最长公共前缀没有到达S串最后的位置的话,按照题意的办法还要多判断一次,注意此时的答案+1

  • 相关阅读:
    zabbix5.0通过ODBC监控oracle数据库 摩天居士
    zabbix通过API批量添加主机 摩天居士
    failed to write [cannot accept incoming IPC connection: [24] Too many open files] into log file 摩天居士
    RTSP/Onvif协议EasyNVR视频平台版本升级注意事项
    EasyNVR级联到EasyGBS国标平台,上级通道状态频繁切换的原因分析与解决
    如何使用EasyNVR平台的EasyStreamClientTool判断视频流是否可播放?
    【操作教程】EasyNVR视频边缘计算网关硬件如何关闭匿名登录?
    EasyNVR智能技术助力“阳光厨房”,构建校园食品安全“防护网”
    EasyNVR无法级联EasyNVS,并报错“a queue was full”的排查与解决办法
    数论和有限域的基本概念
  • 原文地址:https://www.cnblogs.com/stelayuri/p/12506776.html
Copyright © 2020-2023  润新知