好题。
首先建立AC自动机。然后就不会了。
观察到(n)很小,是状压的级别。
考虑将自动机中的(fin)变量升级为(state)变量,表示从当前节点出发,能否到达各字符串。
则(ins)时,有
void ins(int id){
int x=1;
for(int i=0;i<S;i++){
if(!t[x].ch[s[i]-'A'])t[x].ch[s[i]-'A']=++cnt;
x=t[x].ch[s[i]-'A'];
}
t[x].state|=(1<<id);
}
注意到最后一句的变化。
在(build)时,因为该节点的(fail)树上的所有祖先都是可到达的,我们就可以暴力回跳更新(state)。
即
void build(){
for(int i=0;i<26;i++){
if(t[1].ch[i])q.push(t[1].ch[i]),t[t[1].ch[i]].fail=1;
else t[1].ch[i]=1;
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;i++){
if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]);
else t[x].ch[i]=t[t[x].fail].ch[i];
}
int y=t[x].fail;
while(y!=1&&!t[y].state)y=t[y].fail;
t[x].state|=t[y].state;
}
}
就是最后几行,暴力跳到(fail)树中第一个有值的祖先(再往前的祖先的答案已经存在了现在这个祖先的(state)里)。
然后就是爆搜,从根开始,按照字典序bfs。
void bfs(){
Q.push(node(1,0,cnt=1));
vis[1][0]=true;
while(!Q.empty()){
node x=Q.front();Q.pop();
if(x.state==MAXN-1){print(x.id);exit(0);}
for(int i=0;i<26;i++){
int nstate=x.state|t[t[x.pos].ch[i]].state;
if(vis[t[x.pos].ch[i]][nstate])continue;
vis[t[x.pos].ch[i]][nstate]=true;
from[++cnt]=x.id;
way[cnt]=i;
Q.push(node(t[x.pos].ch[i],nstate,cnt));
}
}
}
(node)是一个结构体,第一维(pos)意为这个状态在trie中的节点编号,第二维(state)就是前文所述的“状态”,第三维(id)是它新的编号(在倒推路径时用到)。
(vis)数组储存各个状态有没有被访问过,这可以避免重复搜索。
(from)和(way)是路径数组。
(print())函数调用(from)和(way)输出答案。
((MAXN-1))为((2^n-1)),即全(1)状态。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,cnt=1,S,from[2501000],MAXN;
char s[110],way[2501000];
bool vis[610][5010];
struct AC_Automaton{
int ch[26],state,fail;
}t[610];
void ins(int id){
int x=1;
for(int i=0;i<S;i++){
if(!t[x].ch[s[i]-'A'])t[x].ch[s[i]-'A']=++cnt;
x=t[x].ch[s[i]-'A'];
}
t[x].state|=(1<<id);
}
queue<int>q;
void build(){
for(int i=0;i<26;i++){
if(t[1].ch[i])q.push(t[1].ch[i]),t[t[1].ch[i]].fail=1;
else t[1].ch[i]=1;
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;i++){
if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]);
else t[x].ch[i]=t[t[x].fail].ch[i];
}
int y=t[x].fail;
while(y!=1&&!t[y].state)y=t[y].fail;
t[x].state|=t[y].state;
}
}
struct node{
int pos,state,id;
node(int x=0,int y=0,int z=0){
pos=x,state=y,id=z;
}
};
void print(int pos){
if(from[pos]!=1)print(from[pos]);
putchar(way[pos]+'A');
}
queue<node>Q;
void bfs(){
Q.push(node(1,0,cnt=1));
vis[1][0]=true;
while(!Q.empty()){
node x=Q.front();Q.pop();
if(x.state==MAXN-1){print(x.id);exit(0);}
for(int i=0;i<26;i++){
int nstate=x.state|t[t[x.pos].ch[i]].state;
if(vis[t[x.pos].ch[i]][nstate])continue;
vis[t[x.pos].ch[i]][nstate]=true;
from[++cnt]=x.id;
way[cnt]=i;
Q.push(node(t[x.pos].ch[i],nstate,cnt));
}
}
}
int main(){
scanf("%d",&n),MAXN=(1<<n);
for(int i=0;i<n;i++)scanf("%s",s),S=strlen(s),ins(i);
build();
bfs();
return 0;
}