【状态压缩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

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

posted @ 2018-09-18 19:49  AntiQuality  阅读(243)  评论(0编辑  收藏  举报