[HNOI2006]最短母串问题 AC自动机

题面:洛谷

题解:

  如果我们对这些小串建出AC自动机,那么我们所求的大串就是要求满足遍历过所有AC自动机上的叶子节点,且经过步数最少的串。如果有多个步数相同的串,要输出字典序最小的串。

  在AC自动机上DP。

  因为我们要求所求串内要出现所有给定小串,而小串个数较少,因此我们考虑状压,然后保存下val[i]表示走到这个点,就可以拥有哪些子串。因为一个非终止节点也可能包含给定小串,因此我们要在建好fail之后,继承一个点x的fail[x]所包含的小串,按编号大小更新即可保证在x被更新之前fail[x]已经被更新。

  设f[i][j]表示在AC自动机的第i个节点上,状态为j的最小代价。

  不知你是否注意到这个状态里没有限制长度和字典序?

  其实这是因为我们可以用bfs解决问题,因为走任意一步的代价都是1,也就是每条边的权值都是1,所以如果我们用bfs的顺序来DP,那么谁先DP到拥有所有串,谁就是代价最小的方案。

  那么怎么保证字典序最小?

  我们只需要在bfs的时候从'a'开始枚举即可。

  

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 610
 5 #define ac 4596
 6 #define inf 2139062143
 7 
 8 int n, m, maxn, tmp, head, tail;
 9 int f[AC][ac], q1[AC * ac], q2[AC * ac];
10 short l1[AC][ac], l2[AC][ac];
11 int c[AC][26], val[AC], fail[AC], go[AC], tot;
12 char s[AC];
13 
14 inline void add()
15 {
16     int len = strlen(s + 1), now = 0;
17     for(R i = 1; i <= len; i ++)
18     {
19         int v = s[i] - 'A';
20         if(!c[now][v]) c[now][v] = ++ tot, go[tot] = v;
21         now = c[now][v];
22     }
23     val[now] |= tmp, tmp <<= 1;//还是要|= 的,否则要是有多个相同串在同一个节点结束就不好了,,,,
24 }
25 
26 #define q q1
27 void build()
28 {
29     for(R i = 0; i < 26; i ++)
30         if(c[0][i]) q[++ tail] = c[0][i];
31     while(head < tail)
32     {
33         int x = q[++ head];
34         for(R i = 0; i < 26; i ++)
35         {
36             if(c[x][i]) fail[c[x][i]] = c[fail[x]][i], q[++ tail] = c[x][i];
37             else c[x][i] = c[fail[x]][i];
38         }
39     }    
40     for(R i = 1; i <= tot; i ++) val[i] |= val[fail[i]];
41 }
42 #undef q 
43 
44 void pre()
45 {
46     scanf("%d", &n);
47     maxn = (1 << n) - 1, tmp = 1;
48     memset(f, 127, sizeof(f));
49     for(R i = 1; i <= n; i ++) scanf("%s", s + 1), add();
50 }
51 
52 void bfs()
53 {
54     head = tail = 0;
55     q1[++ tail] = 0, q2[tail] = 0;
56     f[0][0] = 0;
57     while(head < tail)
58     {
59         int x = q1[++ head], sta = q2[head];
60         if(sta == maxn)
61         {
62             head = 0;
63             while(x) 
64             {
65                 q1[++ head] = go[x];
66                 int tmp1 = x, tmp2 = sta;
67                 x = l1[tmp1][tmp2], sta = l2[tmp1][tmp2];
68             }//因为要用2次,但是用了第一次之后x or sta就改变了,所以必须保存到临时变量
69             for(R i = head; i; i --) printf("%c", q1[i] + 'A');
70             return ;
71         }
72         for(R i = 0; i < 26; i ++)
73         {
74             int v = c[x][i], w = sta | val[v];
75             if(f[v][w] == inf) //bfs包括了最短和字典序最小
76             {
77                 q1[++ tail] = v, q2[tail] = w;
78                 f[v][w] = f[x][sta] + 1;
79                 l1[v][w] = x, l2[v][w] = sta;
80             }
81         }
82     }
83 }
84 
85 int main()
86 {
87 //    freopen("in.in", "r", stdin);
88     pre();
89     build();
90     bfs();
91 //    fclose(stdin);
92     return 0;
93 }
View Code

 

posted @ 2018-12-04 22:15  ww3113306  阅读(530)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。