题意:求一个字符串,必须包含给的前n个字符串,不能包含后m个字符串,求最短的答案串。
题解:建立AC自动机,对需要加入的字符串的末尾加入该标号,不能加入的串末尾记录-1,从原点开始bfs,每个节点记录Vis[305][30005]表示该节点已经搜过的需要的字符串的state,总的复杂度是O(sum*(1<<15)*10)。
AC代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#define maxlen 305
#include<algorithm>
using namespace std;
int vis[305][33000];
int q[10000005][2];//位置 状态
int pre[10000005],num[10000005];
struct Trie
{
int next[maxlen][10],fail[maxlen],end[maxlen],root,L;//next记录节点,在这里end指针代表以当前节点为字符串尾的字符串个数
int newnode()
{
for(int i=0;i<10;i++)
next[L][i]=-1;//节点连接的边初始化为-1
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[],int flag,int biaohao)//trie树的建立
{
int l=strlen(buf);
int now=root;
for(int i=0;i<l;i++)
{
if(next[now][buf[i]-'a']==-1)next[now][buf[i]-'a']=newnode();
now=next[now][buf[i]-'a'];
}
if(flag){
end[now]=-1;
}
else end[now]|=(1<<biaohao);
}
void build()//建立ac自动机
{
queue<int>que;
for(int i=0;i<10;i++)
{
if(next[root][i]==-1)next[root][i]=root;
else //若有连边则将节点加入队列 ,并将fail指针指向root
{
fail[next[root][i]]=root;
que.push(next[root][i]);
}
}
while(!que.empty())
{
int now=que.front();
que.pop();
if(end[fail[now]]>0)//状态转移
end[now]|=end[fail[now]];
if(end[fail[now]]==-1)
end[now]=-1;
for(int i=0;i<10;i++)
{
if(next[now][i]==-1) //若无连边,则将该边指向当前节点fail指针指向的相应字符连接的节点
next[now][i]=next[fail[now]][i];
else //若有连边,则将儿子节点的fail指针指向当前节点fail指针指向相应字符接的节点
{
fail[next[now][i]]=next[fail[now]][i];
que.push(next[now][i]); //加入队列继续遍历
}
}
}
}
}ac;
int gra[305][305];
char s[305];
int main()
{
memset(vis,0,sizeof(vis));
vis[0][0]=1;
ac.init();
int n,m;
scanf("%d%d",&n,&m);
int zong=(1<<n)-1;
for(int i=0;i<n;i++)
{
scanf("%s",s);
ac.insert(s,0,i);
}
for(int i=0;i<m;i++)
{
scanf("%s",s);
ac.insert(s,1,0);
}
ac.build();
for(int i=0;i<ac.L;i++){
for(int j=0;j<10;j++){
gra[i][ac.next[i][j]]=j;
}
}
int front,rear;
front=rear=1;
num[front]=0;
pre[front]=-1;
while(front<=rear){
int x=q[front][0];
int state=q[front][1];
front++;
for(int j=0;j<10;j++)
{
int xx=ac.next[x][j];
if(ac.end[xx]<0)continue;
int state1=(state|(ac.end[xx]));
if(!vis[xx][state1]){
vis[xx][state1]=1;
rear++;
q[rear][0]=xx;
q[rear][1]=state1;
pre[rear]=front-1;
num[rear]=xx;
if(state1==zong){
int len=0;
int now=rear;
while(pre[now]!=-1){
s[len++]=gra[num[pre[now]]][num[now]]+'a';
now=pre[now];
}
reverse(s,s+len);
printf("%s\n",s);
return 0;
}
}
}
}
printf("-\n");
return 0;
}