链接:https://ac.nowcoder.com/acm/contest/9984/E
来源:牛客网
学会字符串哈希后,动态规划选手九峰想要出一道解法为字符串哈希题,于是wcy给他口胡了一道题,却把九峰难倒了,你能帮他解决这个问题吗?
给定长度为n的字符串序列a和字符串k,询问a有多少子序列拼接起来等于k。输入描述:
第一行输入一个正整数n(n≤40)和字符串k(∣k∣≤5∗106)
第二行输入n个字符串a1,a2,...,an,表示给定序列
数据保证a中的字符串总长度不超过5∗1e6,输入的所有字符均为小写字母
输出描述:
一行输出一个整数,表示答案
示例1
说明
拼接后等于abcba的子序列有三种:[1,2,4],[3,4],[5]
。 表示前i个定序列匹配到字符串k第j位的所有合法方案, 所以分为取和不取两种选择,这就类似于01背包了,但是比01背包多了一个点就是取之前需要判断它是否能拼接成字符串k。
所以状态转移方程为 :
利用滚动数组,将状态转移方程进一步转化为 :
使用之前的值,所以别忘记了是逆序遍历。
这个就是用每一个字串不断更新f数组
#include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef unsigned long long ll; const int base=131; const int maxn=5e6+100; const int mod=1e9+7; char k[maxn]; char s[maxn]; ll p[maxn]; ll hk[maxn]; ll hs[maxn]; ll dp[maxn]; ll l[maxn]; ll get_hash(int l,int r){ return hk[r]-hk[l-1]*p[r-l+1]; } int main(){ int n; cin>>n; scanf("%s",k+1); int len=strlen(k+1); p[0]=1; for(int i=1;i<=len;i++){ hk[i]=hk[i-1]*base+k[i]; p[i]=p[i-1]*base; } for(int i=1;i<=n;i++){ scanf("%s",s+1); l[i]=strlen(s+1); for(int j=1;j<=l[i];j++){ hs[i]=hs[i]*base+s[j]; } } dp[0]=1; for(int i=1;i<=n;i++){ for(int j=len;j>=l[i];j--){ if(get_hash(j-l[i]+1,j)==hs[i]) dp[j]+=dp[j-l[i]]; } } cout<<dp[len]<<endl; }