BZOJ 1559 JSOI2009 密码 状压dp+AC自动机+搜索
题目链接: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]='\0'; 96 return; 97 } 98 for(int j=0;j<=ac.np;j++) if(f[j][l-1][s]) 99 for(int w=0;w<26;w++) if(ac.to[j][w]==i){ 100 path[len++]=w+'a'; run(j,l-1,s); 101 len--; break; 102 } 103 if(ac.val[i]){ 104 s^=1<<ac.id[i]; 105 for(int j=0;j<=ac.np;j++) if(f[j][l-1][s]) 106 for(int w=0;w<26;w++) if(ac.to[j][w]==i){ 107 path[len++]=w+'a'; run(j,l-1,s); 108 len--; break; 109 } 110 } 111 } 112 void work() 113 { 114 for(int i=0;i<N;i++) ac.ins(S[i],i); 115 ac.getfail(); 116 f[0][0][0]=1; 117 int all=(1<<N)-1; 118 for(int l=0;l<L;l++) 119 for(int i=0;i<=ac.np;i++){ 120 int ori=ac.val[i]?1<<ac.id[i]:0; 121 for(int s=ori;s<=all;s=(s+1)|ori){ 122 if(!f[i][l][s]) continue; 123 for(int w=0;w<26;w++){ 124 int j=ac.to[i][w]; 125 if(ac.val[j]) f[j][l+1][s|(1<<ac.id[j])]+=f[i][l][s]; 126 else f[j][l+1][s]+=f[i][l][s]; 127 } 128 } 129 } 130 LL ans=0; 131 for(int i=0;i<=ac.np;i++) ans+=f[i][L][all]; 132 cout<<ans<<'\n'; 133 if(ans<=42){ 134 for(int i=0;i<=ac.np;i++) 135 if(f[i][L][all]) run(i,L,all); 136 sort(ss+1,ss+ans+1); 137 for(int i=1;i<=ans;i++) puts(ss[i].str); 138 } 139 } 140 int main() 141 { 142 data_in(); 143 work(); 144 return 0; 145 }