【状态压缩dp】1195: [HNOI2006]最短母串
一个清晰的思路就是状压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
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