我差点没死在这道题上…
首先建出AC自动机,然后在每一个字符串的末尾节点用二进制状态记录该字符串已经完整地出现过了,然后设 f ( i , x , z t ) f(i,x,zt) f(i,x,zt)表示长度为 i i i的密码,对应AC自动机上的 x x x节点,当前每个串有没有出现的状态为 z t zt zt的方案数即可DP。
由于最终结果小于 2 63 2^{63} 263,所以我们可以DP的过程中对 2 64 2^{64} 264取模也就是用unsigned long long 自然溢出。
接下来就是解决方案的问题了。假如密码串中有一个不属于任何规定子串的字符,则该字符有26种情况,可以摆在一个字符的前后又有至少2种情况, 2 ∗ 26 = 52 > 42 2*26=52>42 2∗26=52>42,所以密码串中不会有自由字符。假如我们枚举出一个各个子串出现的顺序,则一定要子串们尽可能“首位相融”。因为如果有一个没有达到完全“首位相融”的密码串满足条件,则完全“首位相融”后剩下的地方就可以放自由串。
我一开始枚举全排列然后再构造串,但那样太慢了,加了哈希依然不能过。这是由于从当前排列到下一排列,其实变动的元素很少,但是需要重新构造一次太浪费状态,所以dfs即可。
当然还有一点,如果一个给定子串是另一个给定子串的子串(呃…这个表述…),就应该删掉它,否则在搜方案上会出偏差。但是我没这么干也过了,就懒得加了,以下是HACK我自己的数据:
4 2 abba bb #include<bits/stdc++.h> using namespace std; #define RI register int typedef unsigned long long LL; const LL bas=131; int L,n,SZ,js;string s[12],gg[36285],now; int a[105][26],val[105],bin[12],fail[105],q[105],vis[12]; LL f[27][105][1030],ans,Bas[37],has[37],H[12][12]; void ins(int x) { int now=1; for(RI i=0;i<s[x].length();++i) { int t=s[x][i]-'a'; if(!a[now][t]) a[now][t]=++SZ; now=a[now][t]; } val[now]|=bin[x-1]; } void getfail() { int he=1,ta=1;q[1]=1; while(he<=ta) { int x=q[he];++he; val[x]|=val[fail[x]]; for(RI i=0;i<26;++i) if(a[x][i]) fail[a[x][i]]=a[fail[x]][i],q[++ta]=a[x][i]; else a[x][i]=a[fail[x]][i]; } } void dfs(int x) { if(now.length()>L) return; if(x==n+1) {gg[++js]=now;return;} string kl=now; for(RI i=1;i<=n;++i) { if(vis[i]) continue; int sL=s[i].length(),nL=now.length(); for(RI j=min(nL,sL);j>=0;--j) { if(has[nL]-has[nL-j]*Bas[j]!=H[i][j]) continue; for(RI k=j;k<sL;++k) { now=now+s[i][k],++nL; has[nL]=has[nL-1]*bas+now[nL-1]-'a'; } break; } vis[i]=1,dfs(x+1),now=kl,vis[i]=0; } } void QAQ() { for(RI i=1;i<=n;++i) for(RI j=0;j<s[i].length();++j) H[i][j+1]=H[i][j]*bas+s[i][j]-'a'; Bas[0]=1;for(RI i=1;i<=L;++i) Bas[i]=Bas[i-1]*bas; dfs(1),sort(gg+1,gg+1+js); for(RI i=1;i<=js;++i) if(gg[i]!=gg[i-1]) cout<<gg[i]<<endl; } int main() { scanf("%d%d",&L,&n); SZ=1;for(RI i=0;i<26;++i) a[0][i]=1; bin[0]=1;for(RI i=1;i<=n;++i) bin[i]=bin[i-1]<<1; for(RI i=1;i<=n;++i) cin>>s[i],ins(i); getfail(),f[0][1][0]=1; for(RI i=0;i<L;++i) for(RI x=1;x<=SZ;++x) for(RI zt=0;zt<bin[n];++zt) { if(!f[i][x][zt]) continue; for(RI j=0;j<26;++j) f[i+1][a[x][j]][zt|val[a[x][j]]]+=f[i][x][zt]; } for(RI x=1;x<=SZ;++x) ans+=f[L][x][bin[n]-1]; printf("%llu\n",ans); if(ans<=42) QAQ(); return 0; }