1483:最短母串
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
原题来自:HNOI 2006
给定 n个字符串 S1,S2,?,Sn?? ,要求找到一个最短的字符串 T,使得这 n 个字符串都是 T的子串。
【输入】
第一行是一个正整数 n,表示给定的字符串的个数;
以下的 n行,每行有一个全由大写字母组成的字符串。
【输出】
只有一行,为找到的最短的字符串 T。
在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
【输入样例】
2
ABCD
BCDABC
【输出样例】
ABCDABC
【提示】
数据范围:
对于全部数据,1≤n≤12,1≤∣Si∣≤50。
【题解】
看到n感觉可以状压。
首先建出AC自动机,在AC自动机上按字典序bfs,用book[i][s] 表示在i点,已经过了s的状态,这个点已经走过了。
复杂度O(1<<n * (n*600)).
代码如下:
#include<bits/stdc++.h> using namespace std; const int N=605; int n,cnt,ji[N],ans; char c[55]; struct trie { int er[26],fail,pan; }a[N]; inline void insert(int jia) { int len=strlen(c+1),now=0; for(int i=1;i<=len;i++) { if(!a[now].er[c[i]-'A']) a[now].er[c[i]-'A']=++cnt; now=a[now].er[c[i]-'A']; } a[now].pan|=(1<<jia); } queue <int> que; inline void get_fail() { for(int i=0;i<=25;i++) if(a[0].er[i]) que.push(a[0].er[i]); while(!que.empty()) { int now=que.front();que.pop(); a[now].pan|=a[a[now].fail].pan; for(int i=0;i<=25;i++) { if(a[now].er[i]) { a[a[now].er[i]].fail=a[a[now].fail].er[i]; que.push(a[now].er[i]); } else a[now].er[i]=a[a[now].fail].er[i]; } } } bool book[605][4097]; struct bfs { int fa,shi,zai,s; }b[2457605]; inline void solve() { int head=1,tail=1; book[0][0]=1; while(head<=tail) { int now=b[head].zai,nz=b[head].s; if(nz==(1<<n)-1) { int hu=head; while(hu!=1) { ji[++ans]=b[hu].shi; hu=b[hu].fa; } return; } for(int i=0;i<=25;i++) { int v=a[now].er[i]; int zt=nz|a[v].pan; if(book[v][zt]) continue; book[v][zt]=1; b[++tail]={head,i,v,zt}; } head++; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",c+1); insert(i-1); } get_fail(); solve(); reverse(ji+1,ji+ans+1); for(int i=1;i<=ans;i++) cout<<(char)(ji[i]+'A'); }