【bzoj1195】[HNOI2006]最短母串 AC自动机+状态压缩+BFS最短路

原文地址: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;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/6825226.html