单词 题解(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
",(dp[1]+1)%mod);
    return 0;
}

卷也卷不过,躺又躺不平
原文地址:https://www.cnblogs.com/hunxuewangzi/p/14503199.html