zoukankan      html  css  js  c++  java
  • 【字符串】拓展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

  • 相关阅读:
    thrift 依赖的库
    环球雅思名师公益大讲堂课表2.182.23 雅思资讯 环球雅思
    经验分享band 7.5已经工作的人如何准备雅思考试学习心得雅思频道|太傻网考试频道|常识 辅导 技巧 机经 名师 访谈 交流 资料 试题库
    Chapter 14. Boost.PropertyTree
    少讨论概念,少争论特征、少议论模型;多写代码、多做测试、多做应用。
    澳洲技术移民申请流程
    neo4j——图数据库初探 JDream314的专栏 博客频道 CSDN.NET
    Australia Immigration Network
    boost::property_tree
    海外澳洲技术移民花费一览表(2006年11月完整版) Topboy 博客园
  • 原文地址:https://www.cnblogs.com/stelayuri/p/12506776.html
Copyright © 2011-2022 走看看