九峰与子序列 题解(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;
}
不摆烂了,写题