codeforces822E Liar -- DP+Hash

6 篇文章 0 订阅

fi,j 表示 s 串前i位分成 j 块最多能匹配到t的第几位。
对于 fi,j ,可以更新:

  • fi+1,j=max(fi+1,j,fi,j)
  • fi+lcp,j+1=max(fi+lcp,j+1,fi,j+lcp)

其中 lcp 表示 s[i+1..n] t[fi,j+1..m] 的最长公共前缀,可以用二分+ Hash 求出。
时间复杂度 O(nxlogn)

代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define P1 127
#define M1 1000000007
#define P2 31
#define M2 998244353
//long long s1[N],s2[N],c[N];
long long s1[2][N],s2[2][N],c[2][N];
int i,j,k,n,m,p,f[N][40];
char s[2][N];
inline int Max(int x,int y){
    return x<y?y:x;
}
inline int Min(int x,int y){
    return x<y?x:y;
}
inline bool Check(int l1,int r1,int l2,int r2){
    return ((s1[0][r1]-s1[0][l1-1]*c[0][r1-l1+1])%M1+M1)%M1==((s2[0][r2]-s2[0][l2-1]*c[0][r2-l2+1])%M1+M1)%M1&&
    ((s1[1][r1]-s1[1][l1-1]*c[1][r1-l1+1])%M2+M2)%M2==((s2[1][r2]-s2[1][l2-1]*c[1][r2-l2+1])%M2+M2)%M2;
}
inline int Lcp(int x,int y){
    int l=x,r=Min(n,m+x-y),Mid;
    while(l<=r){
        Mid=l+r>>1;
        if(Check(x,Mid,y,Mid-x+y))l=Mid+1;else r=Mid-1;
    }
    return r;
}
int main(){
    scanf("%d%s%d%s%d",&n,s[0]+1,&m,s[1]+1,&k);
    for(i=c[0][0]=1;i<=n;c[0][i]=c[0][i-1]*P1%M1,i++)s1[0][i]=(s1[0][i-1]*P1+s[0][i]-'a'+1)%M1;
    for(i=1;i<=m;i++)s2[0][i]=(s2[0][i-1]*P1+s[1][i]-'a'+1)%M1;
    for(i=c[1][0]=1;i<=n;c[1][i]=c[1][i-1]*P2%M2,i++)s1[1][i]=(s1[1][i-1]*P2+s[0][i]-'a'+1)%M2;
    for(i=1;i<=m;i++)s2[1][i]=(s2[1][i-1]*P2+s[1][i]-'a'+1)%M2;
    for(i=0;i<=n;i++)
    for(j=0;j<=k;j++){
        f[i+1][j]=Max(f[i][j],f[i+1][j]);
        if(j<k){
            p=Lcp(i+1,f[i][j]+1);
            if(p>i)f[p][j+1]=Max(f[p][j+1],p-i+f[i][j]);
        }
    }
    for(j=1;j<=k;j++)
    if(f[n][j]==m)return printf("YES\n"),0;
    printf("NO\n");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值