单词 题解(dp)

题目链接

题目思路

\(dp[i]\)表示以\(i\)开头的答案

首先预处理如果\(i\)作为区间开头,那么他的右端点最近可以到哪里

那么\(s[i]\)可以选也可以不选

若不选\(dp[i]+=dp[i-1]\)

若选\(dp[i]+=suf[r[i]+1]+d1-r[i]+1(d1代表匹配串的长度)\)

不选\(s[i]\)很容易解释

\(s[i]\)首先可以只有一个区间那么就是\(d1-r[i]+1\)

若有多个区间则为\(dp[r[i]+1]+dp[r[i]+2].....+dp[n]\)

因为以\(s[i]\)开头的区间可以为\([i,r[i]],[i,r[i]+1]....\)

用后缀和优化一下即可

代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e6+5,mod=99824353;
char s[maxn],t[maxn];
int r[maxn];
int suf[maxn];
ll dp[maxn];
ull pre[maxn],temp,power[maxn],base=233;
ull get_hash(int l,int r){
    return pre[r]-pre[l-1]*power[r-l+1];
}
int main(){
    scanf("%s %s",s+1,t+1);
    int d1=strlen(s+1),d2=strlen(t+1);
    power[0]=1;
    for(int i=1;i<=max(d1,d2);i++){
        power[i]=power[i-1]*base;
        if(i<=d1){
            pre[i]=pre[i-1]*base+s[i];
        }
        if(i<=d2){
            temp=temp*base+t[i];
        }
    }
    int pos=0;
    for(int i=d1-d2+1;i>=1;i--){
        if(get_hash(i,i+d2-1)==temp){
            pos=i+d2-1;
        }
        r[i]=pos;
    }
    ll ans=0;
    for(int i=d1;i>=1;i--){
        dp[i]=dp[i+1];
        if(r[i]!=0){
            dp[i]+=suf[r[i]+1]%mod;
            dp[i]+=d1-r[i]+1;
            dp[i]%=mod;
        }
        suf[i]=(suf[i+1]+dp[i])%mod;
    }
    printf("%lld\n",(dp[1]+1)%mod);
    return 0;
}

posted @ 2021-03-08 23:53  hunxuewangzi  阅读(74)  评论(0)    收藏  举报