九峰与子序列 题解(dp+hash)

题目链接

题目大意

给你\(n(n\leq 40)\)个字符串\(a(\sum_{i=1}^{i=n}len[a[i]]\leq 5e6)\)和一个匹配串\(s(len[s]\leq 5e6)\)

要你求有多少种\(a\)的子序列拼接起来等于s

题目思路

个人感觉属于一个套路dp,然后自己居然没想出来。。

\(dp[i][j]\)表示前i个子序列匹配到s串第j位的方案数

\(dp[i][j]=dp[i-1][j]+dp[i-1][j-len[i]]*(a[i]==s.substr(j-len[i]+1,len[i])))\)

判断是否相同用hash处理即可

dp省去第一维,优化空间,注意dp时要倒序

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int maxn=5e6+5,inf=0x3f3f3f3f;
//int mod=1e9+7;
const int eps=1e-3;
ull base=1e9+7;
ull power[maxn],pre[maxn];
ull a[50];
int n,d;
int len[50];
char s[maxn],temp[maxn];
ll dp[maxn];
void init(){//hash前缀
    power[0]=1;//别忘了
    for(int i=1;i<=d;i++){
        power[i]=power[i-1]*base;
    }
    for(int i=1;i<=d;i++){
        pre[i]=pre[i-1]*base+s[i]-'a'+1;
    }
}
ull gethash(int l,int r){
    return pre[r]-pre[l-1]*power[r-l+1];
}
signed main(){
    scanf("%d %s",&n,s+1);
    d=strlen(s+1);
    init();
    for(int i=1;i<=n;i++){
        scanf("%s",temp+1);
        len[i]=strlen(temp+1);
        for(int j=1;j<=len[i];j++){ //预处理每个串的hash值
            a[i]=a[i]*base+temp[j]-'a'+1;
        }
    }
    dp[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=d;j>=len[i];j--){
            // [j-len[i]+1,j]
            if(a[i]==gethash(j-len[i]+1,j)){
                dp[j]+=dp[j-len[i]];
            }
        }
    }
    printf("%lld\n",dp[d]);
    return 0;
}

不摆烂了,写题
原文地址:https://www.cnblogs.com/hunxuewangzi/p/14419262.html