[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 }