题目链接:https://vjudge.net/problem/UVALive-3942
本篇是刘汝佳《算法竞赛入门经典——训练指南》的读书笔记(复述),详见原书 \(P209\) .
解题思路:
先用字典树维护字典中所有的单词。
定义 \(f(x)\) 为以长字符串中第 \(x\) 个字符开始的字符串的分解方案数,则 \(f(x) = sum{f(x+len(i))}\) ,其中 \(i\) 是该字符串的所有前缀。
用一种类似 \(DFS\) 的方式解决这个问题。
注意:书中的模数写错了。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 const ll mod = 20071027; 6 const int maxn = 300000+5; 7 char inp[maxn]; 8 struct Trie{ 9 int ch[400010][30]; 10 int val[400010]; 11 int sz; 12 void inserts(char *s,int v){ 13 int u=0,n=strlen(s); 14 for(int i=0;i<n;i++){ 15 int c=s[i]-'a'; 16 if(!ch[u][c]){ 17 memset(ch[sz],0,sizeof(ch[sz])); 18 val[sz]=0; 19 ch[u][c]=sz++; 20 } 21 u=ch[u][c]; 22 } 23 val[u]=v; 24 } 25 }; 26 Trie tree; 27 ll f[maxn]; 28 29 ll solve(int head,int tail){ 30 if(f[head]!=-1) return f[head]; 31 int now=0; 32 ll ret=0; 33 for(int i=head;i<tail;i++){ 34 int c=inp[i]-'a'; 35 int Next=tree.ch[now][c]; 36 if(Next==0){ 37 f[head]=ret; 38 return ret; 39 } 40 if(tree.val[Next]) 41 ret=(ret+solve(i+1,tail))%mod; 42 now=Next; 43 } 44 f[head]=ret; 45 return ret; 46 } 47 int main(){ 48 char word[110]; 49 int kase=1; 50 while(scanf("%s",inp)==1){ 51 int S; 52 memset(f,-1,sizeof(f)); 53 tree.sz=1; 54 memset(tree.ch[0],0,sizeof(tree.ch[0])); 55 scanf("%d",&S); 56 while(S--){ 57 scanf("%s",word); 58 tree.inserts(word,1); 59 } 60 int tail=strlen(inp); 61 f[tail]=1; 62 printf("Case %d: %lld\n",kase++,solve(0,tail)); 63 } 64 return 0; 65 }