dp+哈希

链接: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

输入

复制
5 abcba
ab c abc ba abcba

输出

复制
3

说明

拼接后等于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;
}

 

posted @ 2021-02-25 17:59  哎呦哎(iui)  阅读(89)  评论(0编辑  收藏  举报