1. 程式人生 > >bzoj1559/洛谷P4045 [JSOI2009]密碼 AC自動機+狀壓DP+搜尋

bzoj1559/洛谷P4045 [JSOI2009]密碼 AC自動機+狀壓DP+搜尋

我差點沒死在這道題上…

首先建出AC自動機,然後在每一個字串的末尾節點用二進位制狀態記錄該字串已經完整地出現過了,然後設f(i,x,zt)f(i,x,zt)表示長度為ii的密碼,對應AC自動機上的xx節點,當前每個串有沒有出現的狀態為ztzt的方案數即可DP。

由於最終結果小於2632^{63},所以我們可以DP的過程中對2642^{64}取模也就是用unsigned long long 自然溢位。

接下來就是解決方案的問題了。假如密碼串中有一個不屬於任何規定子串的字元,則該字元有26種情況,可以擺在一個字元的前後又有至少2種情況,226=52>

422*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; }