题目大意:给出一个长度为n的字符串(n<=3e5)和一个单词表(单词长度不超过100),求该字符串被这些单词表示的方案总数。
不难想到递推算法:令f[s]为表示字符串s的方案总数,若某个单词为s的前缀,f[s]=sum(f[x])(x为s去掉该单词前缀的后缀字符串)
寻找字符串的前缀是trie的经典操作,把所有单词插入trie中,然后在trie上dp即可。
PE了三次...
#include <bits/stdc++.h> using namespace std; typedef long long int LL; #define st first #define nd second #define pb push_back #define mp make_pair #define pll pair <LL, LL> #define pii pair <int, int> #define rep(i,x) for(int i=1;i<=x;i++) const int N = 2e6+7; const int MX = 1e9+7; const LL INF = 1e18+9LL; const int mod = 20071027; string s; int n,cnt,l; int mem[300010]; struct Trie{ int val[400010],ch[400010][26]; int sz=1; void init(){ memset(ch[0],0,sizeof(ch[0])); sz=1; } int index(char c){return c-'a';}; void insert(char* s){ int u=0,l=strlen(s); for(int i=0;i<l;i++){ if(!ch[u][index(s[i])]){ memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; ch[u][index(s[i])]=sz++; } u=ch[u][index(s[i])]; } val[u]=1; } }trie; int solve(int st){ if(st==l)return 1; if(mem[st]!=-1)return mem[st]; int res=0; int u=0; for(int i=st;i<l;i++){ if(trie.ch[u][trie.index(s[i])]){ if(trie.val[trie.ch[u][trie.index(s[i])]]){ res+=solve(i+1),res%=mod; } u=trie.ch[u][trie.index(s[i])]; } else break; } return mem[st]=res; } int main(){ while(cin>>s){ scanf("%d",&n); l=s.size(); for(int i=0;i<l;i++)mem[i]=-1; trie.init(); rep(i,n){ char a[120]; scanf("%s",a); trie.insert(a); //trie.print(0,""); } printf("Case%d:%d\n",++cnt,solve(0)); } }