题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1559
分析:
这个题意真的是很**啊!!!直接说每一个字符串至少出现一次不就好了吗......一开始理解错了ORZ
观察发现这个东西是字符串相关,并且有多个模板串,所有串的长度短并且串的数量不多,最多10个,因此大概可以想到一个AC自动机上面的状压。
首先把被包含的单词去掉,它们对决策不影响,这样在写方程的时候就可以不考虑last了。
令f(i,l,s)表示当位于AC自动机的状态i时,已经生成了l个字符,所有单词的出现情况为s的方案数。
f(i,l,s) = sum{ f(j,l-1,s) | j->i } + sum{ f(j,l-1,s-{i}) | j->i },s包含单词i。刷表实现即可。
ans=sum{ f(i,L,all) | 0<=i<=np }
当答案小于等于42的时候直接在状态转移图上面倒着搜就可以了,看那些状态对当前状态有贡献,一路搜下去,最后把所有串排个序即可。
时间复杂度O(L*N*len*2^N),最后的方案搜索因为方案很少几乎不要时间。
get套路之:字符串算法构造状态辅助字符串有关dp!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 typedef long long LL; 14 15 int L,N,cnt,len; 16 char S[12][12],path[30]; 17 LL f[105][27][1050]; 18 bool vis[12]; 19 struct mstring{ 20 static const int maxn=30; 21 char str[maxn]; 22 mstring(){ memset(str,0,sizeof(str)); } 23 friend bool operator < (mstring a,mstring b){ 24 int n=min(strlen(a.str),strlen(b.str)); 25 for(int i=0;i<n;i++) 26 if(a.str[i]!=b.str[i]) return a.str[i]<b.str[i]; 27 return strlen(a.str)<strlen(b.str); 28 } 29 }ss[50]; 30 struct Aho_Corasick_Automaton{ 31 static const int maxn=105; 32 int np,to[maxn][26],val[maxn],fail[maxn],id[maxn]; 33 Aho_Corasick_Automaton(){ 34 np=0; memset(to[0],0,sizeof(to[0])); 35 } 36 void ins(char *s,int ii){ 37 int p=0,n=strlen(s); 38 for(int i=0;i<n;i++){ 39 int w=s[i]-'a'; 40 if(!to[p][w]){ 41 to[p][w]=++np,val[np]=id[np]=fail[np]=0; 42 memset(to[np],0,sizeof(to[np])); 43 } 44 p=to[p][w]; 45 } 46 val[p]=1,id[p]=ii; 47 } 48 void getfail(){ 49 queue<int>q; 50 fail[0]=0; 51 for(int w=0;w<26;w++) 52 if(to[0][w]) fail[to[0][w]]=0,q.push(to[0][w]); 53 while(!q.empty()){ 54 int p=q.front(); q.pop(); 55 for(int w=0;w<26;w++){ 56 int j=to[p][w]; 57 if(!j){ to[p][w]=to[fail[p]][w]; continue; } 58 q.push(j); 59 fail[j]=to[fail[p]][w]; 60 } 61 } 62 } 63 }ac; 64 65 void data_in() 66 { 67 scanf("%d%d",&L,&N); 68 for(int i=0;i<N;i++) scanf("%s",S[i]); 69 for(int i=0;i<N;i++) if(!vis[i]){ 70 int n=strlen(S[i]); 71 for(int j=0;j<N;j++) if(i!=j){ 72 int m=strlen(S[j]); 73 if(m>n) continue; 74 bool ok; 75 for(int k=0;k<=n-m;k++){ 76 ok=1; 77 for(int l=0;l<m;l++) 78 if(S[i][k+l]!=S[j][l]){ ok=0; break; } 79 if(ok) break; 80 } 81 if(ok) vis[j]=1; 82 } 83 } 84 int tmp=0; 85 for(int i=0;i<N;i++) if(!vis[i]) 86 memcpy(S[tmp++],S[i],sizeof(S[i])); 87 N=tmp; 88 } 89 void run(int i,int l,int s) 90 { 91 if(i==0&&l==0&&s==0){ 92 cnt++; 93 for(int j=len-1;j>=0;j--) 94 ss[cnt].str[len-1-j]=path[j]; 95 ss[cnt].str[len]='