1. 程式人生 > >【狀態壓縮dp】1195: [HNOI2006]最短母串

【狀態壓縮dp】1195: [HNOI2006]最短母串

ring output 之前 狀態壓縮 ems cout 長度 html 字典

一個清晰的思路就是狀壓dp;不過也有AC自動機+BFS的做法

Description

給定n個字符串(S1,S2,„,Sn),要求找到一個最短的字符串T,使得這n個字符串(S1,S2,„,Sn)都是T的子串。

Input

第一行是一個正整數n(n<=12),表示給定的字符串的個數。 以下的n行,每行有一個全由大寫字母組成的字符串。每個字符串的長度不超過50.

Output

只有一行,為找到的最短的字符串T。在保證最短的前提下, 如果有多個字符串都滿足要求,那麽必須輸出按字典序排列的第一個。

Sample Input

2
ABCD
BCDABC

Sample Output

ABCDABC

題目分析

狀壓dp

看到數據範圍,自然想到狀壓dp。$f[t][i]$表示“已經選了$t$這個狀態,第$i$個是最後一個選的”狀態下最短長度。那麽轉移時候就是常規的狀壓dp轉移。

至於處理兩個字符串最長公共前後綴長度,我是用hash去做的。當然在AC自動機上根據fail邊跳也不失為一種好方法。

 1 #include<bits/stdc++.h>
 2 typedef unsigned int uint;
 3 const int maxn = 103;
 4 const
int base = 233; 5 6 std::string str[5003][maxn],s[maxn],sv[maxn],tmp; 7 int n,all,cnt,mn; 8 uint power[maxn]; 9 int lens[maxn],num[maxn][maxn],f[5003][maxn]; 10 11 int main() 12 { 13 memset(f, 0x3f3f3f3f, sizeof f); 14 scanf("%d",&n); 15 all = (1<<n)-1, power[0] = 1
; 16 for (int i=1; i<=n; i++) 17 std::cin >> s[i], lens[i] = s[i].length(); 18 for (int i=1; i<=53; i++) power[i] = power[i-1]*base;  //之前把這個循環放在1..n的循環裏了…… 以後預處理還是要小心數據範圍。 19 for (int i=1; i<=n; i++) 20 for (int j=1; j<=n; j++) 21 if (i^j){ 22 int l = std::min(lens[i], lens[j]); 23 uint val1 = 0, val2 = 0; 24 for (int t=0; t<l; t++) 25 { 26 val1 = val1+power[t]*(s[i][lens[i]-t-1]-A+1); 27 val2 = val2*base+s[j][t]-A+1; 28 if (val1==val2) num[i][j] = t+1; 29 } 30 } 31 mn = f[0][0], f[0][0] = 0; 32 for (int j=1, tst=1; j<=n; j++, tst=1<<(j-1)) 33 f[tst][j] = lens[j], str[tst][j] = s[j]; 34 for (int p=1; p<all; p++) 35 for (int i=1, sst=1; i<=n; i++, sst=1<<(i-1)) 36 if (p&sst) 37 for (int j=1, tst=1; j<=n; j++, tst=1<<(j-1)) 38 if (!(p&tst)){ 39 tmp = str[p][i]+s[j].substr(num[i][j], lens[j]-num[i][j]); 40 if (f[p+tst][j] > f[p][i]+lens[j]-num[i][j]){ 41 f[p+tst][j] = f[p][i]+lens[j]-num[i][j]; 42 str[p+tst][j] = tmp; 43 }else if (f[p+tst][j]==f[p][i]+lens[j]-num[i][j]&&tmp < str[p+tst][j]) 44 str[p+tst][j] = tmp;  //j和tst一開始沒有分清 45 } 46 for (int i=1; i<=n; i++) 47 if (f[all][i] < mn){ 48 mn = f[all][i], cnt = 1; 49 sv[cnt] = str[all][i]; 50 }else if (f[all][i]==mn) sv[++cnt] = str[all][i]; 51 std::sort(sv+1, sv+cnt+1); 52 std::cout << sv[1]; 53 return 0; 54 }

AC自動機+BFS

老早就聽說過這個思路,不過寫完狀壓dp去看題解時候才好好想了想。

這個做法相對來說要抽象一些。不過也算是AC自動機的一種套路應用吧。

這裏按順序枚舉保證了字典序最小;BFS保證了長度最小。

Bzoj1195 [HNOI2006]最短母串 [AC自動機]

END

【狀態壓縮dp】1195: [HNOI2006]最短母串