LA3942 remember the word trie+dp

来源:互联网 发布:php一句话木马使用 编辑:程序博客网 时间:2024/05/19 18:14

题意:给出n个单词组成字典,再给出一个目标字符串,求把这个字符串分解成若干个单词的连接方案(字典中的单词可以重复使用)。

比如:输入 abcd, 再给出四个单词a, b, ab, cd。abcd可以分解为(a, b, cd), 或(ab, cd), 共两种方案

想法为:用dp[i]表示从i开始的后缀,则dp(i)=sum{d(i+len(x))|单词x是s[i...L]的前缀}

具体解释:以abcd为例,先把“a, b, ab, cd”建成一棵前缀树,

首先考虑后缀"d", 它的前缀为“d”, 然后去找字典中有没有这个前缀d,在树中询问,发现没有,所以用字典中单词分解“d”的方案为0;dp[3]=0;

再考虑“cd”, 前缀为“c, cd”。在树中询问,发现只有cd这个前缀,len["cd"]=2, dp[3]=dp[3+len[cd]]=dp[5]=1;

考虑“bcd”,前缀为“b, bc, bcd”, 只有前缀“b”, 那么dp[2]=dp[2+1]=1;

最后“abcd”, 前缀“a, b, ab, cd”, 有前缀“a”和“ab”,当固定“a”,存在的方案为"bcd"的分解方案,即从1开始的后缀的分解数目,为1;当固定“ab”时,存在的方案为“cd”的分解方案,即从2开始的后缀的分解方案数,为dp[2]=1;以上加起来,得dp[0]=2;

然后输出dp[0];


虽然白书上面有分析,搞了好久才看懂dp(i)=sum{d(i+len(x))}真心智商捉急,一把辛酸泪_(:з」∠)_ …………

觉得搞char型数组实在好麻烦,于是用了string来存字符串,一度担心过可能会T,不过还是过了,真是QvQ,点赞……

#include<iostream>#include<string>#include<cstring>#include<algorithm>using namespace std;const int maxnode=500000;const int sigma_size=27, mod=20071027;int dp[maxnode];struct Trie{int ch[maxnode][sigma_size];int val[maxnode];int sz;void init(){sz=1;memset(ch[0], 0, sizeof(ch[0]));}int idx(char x){return x-'a';}void insert(string &s, int v){int u=0, n=s.size();for(int i=0;i<n;i++){int c=idx(s[i]);if(!ch[u][c]){memset(ch[sz], 0, sizeof(ch[sz]));val[sz]=0;ch[u][c]=sz++;}u=ch[u][c];}val[u]=v;}int query(string &s, int start){int u=0, ret=0;int len=s.size();for(int i=start;i<len;i++){int c=idx(s[i]);u=ch[u][c];if(u==0)return ret;if(val[u]){ret+=dp[i+1];ret%=mod;}}return ret;}}trie;int main(){string target, tmp;int s;int cnt=0;while(cin>>target){cnt++;memset(dp, 0, sizeof(dp));cin>>s;trie.init();for(int i=0;i<s;i++){tmp.clear();cin>>tmp;trie.insert(tmp, 1);}int len=target.size();dp[len]=1;for(int i=1;i<=len;i++)dp[len-i]=trie.query(target, len-i);printf("Case %d: %d\n",cnt, dp[0]);target.clear();}return 0;}


原创粉丝点击