扩展KMP+矩阵快速幂

20 篇文章 0 订阅
16 篇文章 0 订阅

扩展KMP:给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0<=i<lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i+z-1]==B[0..z-1]的最大的z值)。扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。

【算法】
    

    设next[i]为满足B[i..i+z-1]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。

    设p为目前A串中匹配到的最远位置,k为让其匹配到最远位置的值(或者说,k是在0<=i0<i的所有i0值中,使i0+ex[i0]-1的值最大的一个,p为这个最大值,即k+ex[k]-1),
    
    显然,p之后的所有位都是未知的,也就是目前还无法知道A[p+1..lenA-1]中的任何一位和B的任何一位是否相等。
    
    根据ex的定义可得,A[k..p]==B[0..p-k],因为i>k,所以又有A[i..p]==B[i-k..p-k],设L=next[i-k],则根据next的定义有B[0..L-1]==B[i-k..i-k+L-1]。考虑i-k+L-1与p-k的关系:
    
    (1)i-k+L-1<p-k,即i+L<=p。这时,由A[i..p]==B[i-k..p-k]可以得到A[i..i+L-1]==B[i-k..i-k+L-1],又因为B[0..L-1]==B[i-k..i-k+L-1]所以A[i..i+L-1]==B[0..L-1],这就说明ex[i]>=L。又由于next的定义可得,
 
A[i+L]必然不等于B[L](否则A[i..i+L]==B[0..L],因为i+L<=p,所以A[i..i+L]==B[i-k..i-k+L],这样B[0..L]==B[i-k..i-k+L],故next[i-k]的值应为L+1或更大),这样, 可以直接得到ex[i]=L!
    
    (2)i+k-L+1>=p-k,即i+L>p。这时,首先可以知道A[i..p]和B[0..p-i]是相等的(因为A[i..p]==B[i-k..p-k],而i+k-L+1>=p-k,由B[0..L-1]==B[i-k..i-k+L-1]可得B[0..p-i]==B[i-k..p-k],即A[i..p]==B[0..p-i]),然
 
后,对于A[p+1]和B[p-i+1]是否相等,目前是不知道的(因为前面已经说过,p是目前A串中匹配到的最远位置,在p之后无法知道任何一位的匹配信息),因此,要从A[p+1]与B[p-i+1]开始往后继续匹配(设j为目前
 
B的匹配位置的下标,一开始j=p-i+1,每次比较A[i+j]与B[j]是否相等,直到不相等或者越界为止,此时的j值就是ex[i]的值)。在这种情况下,p的值必然会得到延伸,因此更新k和p的值。
    
    边界:ex[0]的值需要预先求出,然后将初始的k设为0,p设为ex[0]-1。
 
对于求next数组,也是“自身匹配”,类似KMP的方法处理即可。唯一的不同点也在边界上:可以直接知道next[0]=lenB,next[1]的值预先求出,然后初始k=1,p=ex[1]。

需要严重注意的是,在上述的情况(2)中,本该从A[p+1]与B[p-i+1]开始匹配,但是,若p+1<i,也就是p-i+1<0(这种情况是有可能发生的,当ex[i-1]=0,且前面的ex值都没有延伸到i及以后的时候)的话,需要将A、B的下标都加1(因为此时p必然等于i-2,如果A、B的下标用两个变量x、y控制的话,x和y都要加1)!!


下面的链接讲得很清楚:扩展KMP


下面是acdream群赛的题目:Gao the string


如果知道了ext[i]表示i开始的后缀s[i....n]跟前缀s[1...]之间的 公共的前缀,那么以i开头的后缀中就匹配了ext[i]个前缀了

所以i这个后缀出现的前缀的数量实际上就是ext[i] + ext[i+1] + .. ext[n]. 求出来之后加个矩阵马甲就好了

求lcp的时候可以二分+hash,也可以用扩展KMP,也可以用后缀数组。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int MOD=1000000007;
char str[maxn];
int n;
long long next[maxn];
void Extent_KMP()//求next数组
{
    next[0]=n;
    int j=0;
    while(j+1<n&&str[j]==str[j+1])j++;
    next[1]=j;
    int k=1;
    for(int i=2;i<n;i++)
    {
        int p=k+next[k]-1;
        int l=next[i-k];
        if(i+l<p+1)next[i]=l;//第一种情况
        else//第二种情况
        {
            j=max(0,p-i+1);
            while(i+j<n&&str[i+j]==str[j])j++;
            next[i]=j;
            k=i;
         }
    }
}
struct Matrix
{
    int m[5][5];
    Matrix operator *(const Matrix & a)const
    {
        Matrix ret;
        for(int i=0;i<2;i++)
        {
            for(int j=0;j<2;j++)
            {
                ret.m[i][j]=0;
                for(int k=0;k<2;k++)
                {
                    ret.m[i][j]+=(long long)m[i][k]*a.m[k][j]%MOD;
                    if(ret.m[i][j]>=MOD)ret.m[i][j]-=MOD;
                }
            }
        }
        return ret;
    }
};
Matrix pow_m(Matrix a,long long x)
{
    Matrix ret;
    memset(ret.m,0,sizeof(ret.m));
    ret.m[0][0]=ret.m[1][1]=1;
    while(x)
    {
        if(x&1)ret=ret*a;
        a=a*a;
        x>>=1;
    }
    return ret;
}
int cal(long long x)
{
    Matrix A;
    A.m[0][0]=0;
    A.m[0][1]=A.m[1][0]=A.m[1][1]=1;

    A=pow_m(A,x);
    return A.m[1][0];
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%s",str)!=EOF)
    {
        n=strlen(str);
        Extent_KMP();
        for(int i=n-2;i>=0;i--)next[i]+=next[i+1];
        long long ans=0;
        for(int i=0;i<n;i++)
        {
            ans+=cal(next[i]);
            if(ans>=MOD)ans-=MOD;//如果用ans%=MOD会超时
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值