https://vjudge.net/problem/UVALive-3942
题意:给出一个字典和一个字符串,将这个字符串分解成若干个单词的连接(单词可以重复使用),问有多少种分法
dp[i]表示以i开始(即后缀[i,L])的字符串的分解方案数
dp[i]=Σ dp[i+len[x]] (x是[i,L]的前缀)
判断x是否是[i,L]的前缀:
构造字典树,
如果在i发现了单次结尾,dp[]+=dp[i+1],不需要字典树每个节点挂一个链表
#include<cstdio> #include<cstring> #define N 300001 #define M 101 #define mod 20071027 using namespace std; char s[N],a[M]; int m,len; int trie[M*4001][27],tot; int dp[N]; int mark[M*4001]; void insert(int j) { len=strlen(a); int root=0,id; for(int i=0;i<len;i++) { id=a[i]-'a'; if(!trie[root][id]) trie[root][id]=++tot; root=trie[root][id]; } mark[root]=j; } void find(int start,int en) { int root=0,id,ans=0; for(int i=start;i<=en;i++) { id=s[i]-'a'; if(!trie[root][id]) break ; root=trie[root][id]; if(mark[root]) { ans+=dp[i+1]%mod; ans%=mod; } } dp[start]=ans; } int main() { int t=0; while(scanf("%s",s)!=EOF) { scanf("%d",&m); memset(dp,0,sizeof(dp)); memset(mark,0,sizeof(mark)); memset(trie,0,sizeof(trie)); for(int i=1;i<=m;i++) { scanf("%s",a); insert(i); } int l=strlen(s); dp[l]=1; for(int i=l-1;i>=0;i--) find(i,l-1); printf("Case %d: %d\n",++t,dp[0]); } }