刘汝佳新书--训练指南
题意:给出N个不同单词和一个长字符串S。把这个字符串分解成若干个单词的连接(单词尅重复使用),问有多少种方法?
分析:令d[i]表示从字符i开始的字符串的分解方案数,则dans[i]=sum{dans[i+d[x]] | 单词x是S[i...len]的前缀};
// File Name: 1401.cpp // Author: zlbing // Created Time: 2013/3/14 18:55:52 #include<iostream> #include<string> #include<algorithm> #include<cstdlib> #include<cstdio> #include<set> #include<map> #include<vector> #include<cstring> #include<stack> #include<cmath> #include<queue> using namespace std; #define CL(x,v); memset(x,v,sizeof(x)); #define INF 0x3f3f3f3f #define LL long long #define REP(i,n) for(int i=0;i<n;i++) #define REP1(i,n) for(int i=1;i<n+1;i++) const int maxnode=4000*100+10; const int sigma_size=26; // 字母表为全体小写字母的Trie struct Trie { int ch[maxnode][sigma_size]; int val[maxnode]; int sz; // 结点总数 void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点 int idx(char c) { return c - 'a'; } // 字符c的编号 // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点” void insert(const char *s, int v) { int u = 0, n = strlen(s); 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; // 中间结点的附加信息为0 ch[u][c] = sz++; // 新建结点 } u = ch[u][c]; // 往下走 } val[u] = v; // 字符串的最后一个字符的附加信息为v } void find_prefix(char *s,int len,vector<int>& ans){ // int len=strlen(s);每次找前缀时都算一次长度,要算10^5次,因此导致超时了。。。又这长度是有规律的,因此可以事先算出来 int u=0; for(int i=0;i<len;i++){ if(s[i]=='\0')break; int c=idx(s[i]); if(ch[u][c]){ u=ch[u][c]; if(val[u])ans.push_back(val[u]); }else break; } } }; Trie solver; char str[300050]; int d[4005]; int dans[300050]; const int MOD=20071027; int main(){ int cas=0; while(~scanf("%s",str)){ int n; scanf("%d",&n); solver.clear(); char word[105]; REP1(i,n){ scanf("%s",word); int len=strlen(word); d[i]=len; solver.insert(word,i); } int len=strlen(str); CL(dans,0); dans[len]=1; for(int i=len-1;i>=0;i--){ vector<int> ans; solver.find_prefix(str+i,len-i,ans); for(int j=0;j<ans.size();j++){ dans[i]=(dans[i]+dans[i+d[ans[j]]])%MOD; } } printf("Case %d: %d\n",++cas,dans[0]); } return 0; }