思路:
首先对模式串构建AC自动机,在建立Trie时标记模式串的终止结点,并记录每个串的长度,在AC自动机上对原串进行匹配,同时将每个状态加入栈中,若后续成功匹配,则将状态出栈至加入该串之前的状态,再从栈顶状态开始继续匹配,重复该步骤,直到匹配到原串结尾,最后栈中保存的即为答案。
注意:
使用string会发生段错误,改为char[]就没问题了,原因未知。。。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1000005;
const int MAXC=26;
int p=0;
struct AC_Automaton {
int trie[MAXN][MAXC], fail[MAXN], sta[MAXN],cnt[MAXN],last[MAXN];
int tot;
queue<int>Q;
void init() {
while(!Q.empty()) Q.pop();
memset(trie, -1, sizeof(trie));
memset(fail, 0, sizeof(fail));
tot = 0;
memset(sta, 0, sizeof(sta));
memset(cnt,0,sizeof(cnt));
memset(last,0,sizeof(last));
memset(S,0,sizeof(S));
}
void insert(char *ch) { //插入字符串到字典树中
int rt = 0,i;
for (i = 0; ch[i]; i++)
{
if (trie[rt][ch[i] - 'a'] == -1) trie[rt][ch[i] - 'a']= ++tot;
rt = trie[rt][ch[i] - 'a'];
}
sta[rt]=i; //记录该串长度
cnt[rt]++; //标记该串终止结点
}
void build() //构建fail指针数组,相当于next数组,利用bfs求
{
for(int i=0;i<MAXC;i++) //初始化
if(trie[0][i]==-1)
trie[0][i]=0; //将不存在的点指向根节点
else
Q.push(trie[0][i]); //将与根节点直接相连的点入队
while(!Q.empty())
{
int rt=Q.front();
Q.pop();
for(int i=0;i<MAXC;i++)
{
if(trie[rt][i]==-1) //某一点无后续节点,将其连向失配指针所在位置
trie[rt][i]=trie[fail[rt]][i];
else //有后续节点
{
int v=fail[rt];
int u=trie[rt][i];
while(v && trie[v][i]==-1) v=fail[v]; //其失配指针是从其父亲失配指针指向位置向后搜索i,若有则连接
fail[u]=trie[v][i];
Q.push(u);
last[u]=cnt[u]?u:last[fail[u]]; //若失配存储失配后的位置,若匹配成功,存储当前结点
}
}
}
}
int S[MAXN]; //模拟栈
void solve(char *ch)
{
int rt=0,top=0;
for(int i=0;ch[i];i++)
{
ch[p++]=ch[i]; //p为当前位置指针,
rt=trie[rt][ch[i]-'a'];
S[top++]=rt; //将当前状态入栈
int tmp=last[rt];
if(cnt[tmp]>0) //若匹配成功
{
p-=sta[tmp]; //将匹配成功的串出栈
top-=sta[tmp];
rt=S[top-1]; //从栈顶元素开始重新匹配
}
}
return ;
}
}T;
int main()
{
char a[100005];
char s[100005];
int n;
T.init();
scanf("%s",s);
cin>>n;
while(n--)
{
scanf("%s",a);
T.insert(a);
}
T.build();
T.solve(s);
s[p]='\0';
printf("%s\n",s);
return 0;
}