bzoj 1195 [HNOI2006]最短母串 bfs 状压 最短路 AC自动机
LINK:最短母串
求母串的问题。不适合SAM。
可以先简化问题 考虑给出的n个字符串不存在包含关系。
那么 那么存在的情况 只可能有 两个字符串拼接起来能表示另外一个字符串 或者某个字符串的后缀可以当成别的字符串的前缀使用。
实际情况可能更加复杂。
观察上面的两种情况 容易AC自动机可以解决类似的问题。
在AC自动机上跑 就可以借用别的字符串的后缀当做自己的前缀进行使用了。
考虑建立出AC自动机 我们要求出一条路径 使得AC自动机上所有的终止节点都被经过。
当然也有特殊情况 就是 以终止节点为fail的节点 或者 以这个节点为fail的节点被经过。
考虑如何求出最短的路径。
可以发现这是一个最短路的问题。当做分层图来跑最短路。
对于上述的两种情况 由于是在 AC自动机的trie图上跑 所以可以满足。
考虑字典序 每次选择节点从字典序最小的选即可。
注意要预处理出s[x]数组 表示x这个节点所表示的所有终止节点的集合 注意 fail指针的s可以传递过来。
const int MAXN=610,maxn=1<<12;
int n,cnt,maxx;
char a[13][MAXN],b[MAXN],ans[MAXN];
int s[MAXN],q[1500010],vis[MAXN][maxn],pre[MAXN][maxn],w[1500010];
struct wy{int ch[26];int fail;}t[MAXN];
inline void insert(int x)
{
int len=strlen(a[x]+1);
int p=0;
rep(1,len,i)
{
int w=a[x][i]-'A';
if(!t[p].ch[w])t[p].ch[w]=++cnt;
p=t[p].ch[w];b[p]=a[x][i];
}
s[p]=s[p]|(1<<(x-1));
}
inline void get_fail()
{
int l=0,r=0;
rep(0,25,i)if(t[0].ch[i])q[++r]=t[0].ch[i];
while(++l<=r)
{
int x=q[l];
rep(0,25,i)
{
int tn=t[x].ch[i];
if(tn)fail(tn)=t[fail(x)].ch[i],s[tn]|=s[t[fail(x)].ch[i]],q[++r]=tn;
else t[x].ch[i]=t[fail(x)].ch[i];
}
}
}
inline void bfs()
{
int l=0,r=0;q[++r]=0;
vis[0][0]=0;w[r]=0;
while(++l<=r)
{
int x=q[l];
int xx=w[l];
rep(0,25,i)
{
int tn=t[x].ch[i];
if(tn)
{
int ss=s[tn]|xx;
if(vis[tn][ss]==-1)
{
pre[tn][ss]=l;
vis[tn][ss]=vis[x][xx]+1;
q[++r]=tn;w[r]=ss;
if(ss==maxx)
{
int w1=tn,w2=ss;
while(vis[tn][ss])
{
ans[vis[tn][ss]]=b[w1];
--vis[tn][ss];
int s1=q[pre[w1][w2]];
int s2=w[pre[w1][w2]];
w1=s1;w2=s2;
}
printf("%s",ans+1);
return;
}
}
}
}
}
}
int main()
{
freopen("1.in","r",stdin);
gt(n);maxx=(1<<n)-1;
rep(1,n,i)gc(a[i]),insert(i);
get_fail();
memset(vis,-1,sizeof(vis));
bfs();return 0;
}