题解 P2322【[HNOI2006]最短母串问题】
题意
给出 $n$ 个互不包含的字符串 $a_{1,2,...,n}$,要求你求出一个字符串 $S$,使得 $\forall i\in[1,n],a_i\in S$,求 $\min|S|$。
$|a_i|\le50,1\le n\le 12$.
思路
首先我们看到有 $n$ 个字符串,感觉 $\text{KMP}$ 不怎么好做,于是我们考虑 $\text{ACAM}$。
然后这个 $n\le 12$ 非常奇怪,于是考虑状压 $\text{dp}$。
我们给每个节点赋值 $state_i$,若在 $\text{Trie}$ 上 $i$ 串以 $j$ 节点结尾则 $state_j\gets state_j\text{ or }2^{i-1}$,在建出 $\text{Fail}$ 树后,将 $state$ 再 $\text{or}$ 上当前节点的所有后缀。
然后我们从根节点开始 $\text{BFS}$,当按照字典序遍历到一个节点时,将当前状态 $\text{or}$ 上节点的 $statu$,若取到全局则将路径上的字符输出即可。
时间复杂度 $O(2^n\sum|a_i|)$。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=600+10,C=(1<<12)+1,S=N*C;
#define Tr ACAM.ch
int n,ans[S],pre[S],state[N],all;
char str[N];
bool vis[N][C];
struct node{
int pos,state;
node(int x=0,int y=0){pos=x,state=y;};
};
queue<node>q;
struct AC_AutoMaton{
int fail[N];
int ch[N][26],cnt=0;
inline void insert(char *s,int n,int x){
int cur=0;
for(int i=1;i<=n;i++){
int x=s[i]-'A';
if(!ch[cur][x]) ch[cur][x]=++cnt;
cur=ch[cur][x];
}
state[cur]|=1<<x-1;
}
inline void getfail(){
queue<int>q;
for(int i=0;i<26;i++){
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=0;i<26;i++){
if(!ch[cur][i]){
ch[cur][i]=ch[fail[cur]][i];
}else{
int v=fail[cur];
fail[ch[cur][i]]=ch[v][i];
state[ch[cur][i]]|=state[ch[v][i]];
q.push(ch[cur][i]);
}
}
}
}
}ACAM;
int main(){
n=read();
all=(1<<n)-1;
for(int i=1;i<=n;i++){
readstr(str);
ACAM.insert(str,strlen(str+1),i);
}
ACAM.getfail();
q.push({0,0});
vis[0][0]=1;
int cnt=0,now=0;
while(!q.empty()){
node cur=q.front();
q.pop();
if(cur.state==all){
stack<char>st;
while(now){
st.push(ans[now]+'A');
now=pre[now];
}
while(!st.empty()) putc(st.top()),st.pop();
return flush(),0;
}
for(int i=0;i<26;i++){
int x=Tr[cur.pos][i],sta=cur.state;
if(!vis[x][sta|state[x]]){
vis[x][sta|state[x]]=1;
q.push({x,sta|state[x]});
pre[++cnt]=now;
ans[cnt]=i;
}
}
now++;
}
}

浙公网安备 33010602011771号