思路:AC自动机的模板题。可是这个题目给的数据范围特别大,算一下空间复杂度,直接超内存了。注意这里不要用memset一次性清空数组,超内存是一次性memset数组导致的,将没有用的空间也用上了,于是试了下不一次性memset数组,果断不超了。每一次匹配成功,做出相应标记时,要标记整个匹配的串的范围,最后一遍扫过去即可。详见代码。代码只是提供大致思路。
TLE代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+1;
int trie[maxn][26]; //字典树
int fail[maxn]; //失败时的回溯指针
int cnt = 0;
int dep[maxn];
int vs[maxn];
void init() {
memset(trie[0],0,sizeof(trie[0]));
memset(fail,0,sizeof(fail));
for(int i=0;i<cnt;i++)
fail[i]=dep[i]=vs[i]=0;
cnt = 0;
///memset(dep,0,sizeof(dep));
///memset(vs,0,sizeof(vs));
}
void insertWords(char *s)
{
int l=strlen(s);
int root = 0;
for(int i=0; i<l; i++)
{
int next = s[i] - 'a';
if(!trie[root][next])
memset(trie[cnt+1],0,sizeof(trie[cnt+1])),
trie[root][next] = ++cnt;
root = trie[root][next];
}
dep[root]=l;
}
void getFail()
{
queue <int>q;
for(int i=0; i<26; i++) //将第二层所有出现了的字母扔进队列
{
if(trie[0][i])
{
fail[trie[0][i]] = 0;
q.push(trie[0][i]);
}
}
while(!q.empty())
{
int now = q.front();
q.pop();
for(int i=0; i<26; i++)
{
if(trie[now][i])
{
fail[trie[now][i]] = trie[fail[now]][i];
q.push(trie[now][i]);
}
else
trie[now][i] = trie[fail[now]][i];
}
}
}
void query(char *s)
{
char ss;
int now = 0,ans = 0;
int l=strlen(s);
for(int i=0; i<l; i++) //遍历文本串
{
ss=s[i];
if('A'<=ss&&ss<='Z') ss+=32;
else if('a'<=ss&&ss<='z') int kk;
else continue;
now = trie[now][ss-'a']; //从s[i]点开始寻找
for(int j=now; j; j=fail[j])
{
if(dep[j])
{
vs[i-dep[j]+1]--,vs[i+1]++;
}
}
}
int sum=0;
for(int i=0; i<strlen(s); i++)
{
sum+=vs[i];
if(sum<0) printf("*");
else printf("%c",s[i]);
}
printf("\n");
}
char s[maxn];
int main()
{
int n,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<n; i++)
{
scanf("%s",s);
insertWords(s);
}
fail[0] = 0;
getFail();
getchar();
gets(s);
///fgets(s, maxn, stdin);
query(s);
init();
}
return 0;
}