LA 3942 - Remember the Word 字典树+DP
给出一个由S个不同单词组成的字典和一个长字符串,把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法?比如有4个单词a 、b 、cd、ab,则abcd有两种分解方法:a+b+cd和ab+cd
用DP来做的话,设dp[i]表示从i开始的字符串到结束的分解方案数,则d[i]=sum{d[ i + len(x)] 单词x是 S[i ……len-1]的前缀。。
故从右向左看,枚举前缀,如果有这个单词则加上d[i]。
PS:用数组实现的话会更快点,我觉得指针好写。。。还有就是这题不释放空间也可以过。(也更快点)。
#include<cstdio> #include<cstring> const int MAXN=300000+10; const int MLEN=26; const int mod=20071027; char s[MAXN],word[MAXN]; int dp[MAXN]; struct node { node* next[MLEN]; bool isEnd; node(){ memset(next,0,sizeof(next)); isEnd=false;} }; struct Trie { node *root; inline int index(char &c){return c-'a';} Trie() {root=new node;} void init() {root=new node;} void insert(char *str) { node *p=root; int len=strlen(str); for(int i=0;i<len;i++) { int id=index(str[i]); if(p->next[id]==NULL) { node *t=new node; p->next[id]=t; } p=p->next[id]; if(i==len-1) { p->isEnd=true; return; } } } void query(char *str,int start) { int len=strlen(str); node *p=root; int res=0; for(int i=start;i<len;i++) { int id=index(str[i]); if(p->next[id]==NULL) break; p=p->next[id]; if(p->isEnd) { res+=dp[i+1]; res%=mod; } } dp[start]=res; } void del(node *root) { if(root==NULL) return; for(int i=0;i<26;i++) if(root->next[i]!=0) del(root->next[i]); delete root; } }trie; int main() { int kase=1; while(scanf("%s",s)!=EOF) { //初始化trie memset(dp,0,sizeof(dp)); trie.init(); int n; scanf("%d",&n); while(n--) { scanf("%s",word); trie.insert(word); } int len=strlen(s)-1; dp[len+1]=1; for(int i=len;i>=0;i--) { trie.query(s,i); // printf("%d\n",dp[i]); } printf("Case %d: %d\n",kase++,dp[0]); trie.del(trie.root); } }
新 blog : www.hrwhisper.me