原文地址:http://www.cnblogs.com/GXZlegend/p/6825226.html
题目描述
给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。
输入
第一行是一个正整数n(n<=12),表示给定的字符串的个数。以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.
输出
只有一行,为找到的最短的字符串T。在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
样例输入
2
ABCD
BCDABC
样例输出
ABCDABC
题解
AC自动机+BFS
先求出fail数组和Trie图,同时标记每个位置是哪些字符串的结尾,用二进制储存。
要求包含所有串的最小长度,需要记录当前包含哪些串,需要状压。
当到达字符串结尾时,更改状态。
所以Trie图可以看成一个分层图,所求为0,1到(1<<n)-1,i的最短路。
由于每条路径代表一个字符,所以边权为1,可以使用BFS求最短路得解。
#include <cstdio> #include <cstring> char str[60] , ch[610] , ans[610]; int tot = 1 , next[610][26] , fail[610] , val[610] , qx[1500010] , qy[1500010] , l , r , dis[610][4096] , fromp[610][4096]; void build() { int x , i; for(i = 0 ; i < 26 ; i ++ ) next[0][i] = 1; l = r = 1 , qx[1] = 1; while(l <= r) { x = qx[l ++ ]; for(i = 0 ; i < 26 ; i ++ ) { if(next[x][i]) fail[next[x][i]] = next[fail[x]][i] , val[next[x][i]] |= val[fail[next[x][i]]] , qx[++r] = next[x][i]; else next[x][i] = next[fail[x]][i]; } } } int main() { int n , l , i , j , x , y; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) { scanf("%s" , str + 1) , l = strlen(str + 1) , x = 1; for(j = 1 ; j <= l ; j ++ ) { if(!next[x][str[j] - 'A']) next[x][str[j] - 'A'] = ++tot; x = next[x][str[j] - 'A'] , ch[x] = str[j]; } val[x] |= 1 << (i - 1); } build(); memset(dis , -1 , sizeof(dis)); l = r = 1 , qx[1] = 1 , qy[1] = 0 , dis[1][0] = 0; while(l <= r) { x = qx[l] , y = qy[l]; for(i = 0 ; i < 26 ; i ++ ) { int tx = next[x][i] , ty = y | val[tx]; if(dis[tx][ty] == -1) { dis[tx][ty] = dis[x][y] + 1 , fromp[tx][ty] = l; if(ty == (1 << n) - 1) { int px = tx , py = ty , tmp; for(i = dis[tx][ty] ; i ; i -- ) ans[i] = ch[px] , tmp = fromp[px][py] , px = qx[tmp] , py = qy[tmp]; printf("%s " , ans + 1); return 0; } qx[++r] = tx , qy[r] = ty; } } l ++ ; } return 0; }