题意是:给你一些模式串,再给一个文本串,如果在文本串中能够查询到该模式串,就将文本串中的模式串改为 '*'
注意可能会爆内存和字符串的输入。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1000005;
struct AC{
int ch[maxn][26];
int cnt[maxn]; //记录模式串的长度
int fail[maxn]; //失败指针
int tot;
int q[maxn]; //q[maxn]在build方法中使用,建立失败指针时会用到,功能和队列差不多,
void init(){ //放在build()方法里面,开的数组太大,导致数组溢出,就放结构体了
memset(ch[0],-1,sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
memset(fail,0,sizeof(fail));
tot=0;
}
int newnode()
{
tot++;
memset(ch[tot],-1,sizeof(ch[tot]));
fail[tot]=0;
cnt[tot]=0;
return tot;
}
void insert(char *s){
int p=0;
for(int i=0;s[i];i++){
if(ch[p][s[i]-'a']==-1)
ch[p][s[i]-'a']=newnode();
p=ch[p][s[i]-'a'];
}
cnt[p]=strlen(s); //记录下该模式串的长度,在接下来的文本串查找中,将会使用。
}
/*void insert(char *s){ //一开始选择使用这种插入方法,导致内存直接溢出,大概84000k
int p=0; //,换了插入之后48000k如果有更好的插入方法,欢迎指出
for(int i=0;s[i];i++){
if(ch[p][s[i]-'a']==-1)
ch[p][s[i]-'a']=++tot;
p=ch[p][s[i]-'a'];
}
cnt[p]=strlen(s);
}*/
void build(){ //建立失败指针,一般使用队列,怎么说,用l和r这种方法和队列各有好坏吧!
int l=0,r=0;
for(int i=0;i<26;i++){
if(ch[0][i]==-1)
ch[0][i]=0;
else{
q[r++]=ch[0][i];
}
}
while(l<r){
int p=q[l++];
for(int i=0;i<26;i++){
if(ch[p][i]==-1){
ch[p][i]=ch[fail[p]][i];
}else{
fail[ch[p][i]]=ch[fail[p]][i];
q[r++]=ch[p][i];
}
}
}
}
void count(char *s){
int p=0;
for(int i=0;s[i];i++){
if(s[i]>='A'&&s[i]<='Z'||s[i]>='a'&&s[i]<='z')
{
if(s[i]>='A'&&s[i]<='Z')
p=ch[p][s[i]-'A'];
else
p=ch[p][s[i]-'a'];
int tmp=p;
while(tmp!=0){ //如果存在该模式串
for(int j=0;j<cnt[tmp];j++)//从模式串的末尾,依次改到模式串的首位
s[i-j]='*';
tmp=fail[tmp];
}
}
}
}
}ac;
char str[1000005],s[1000005];
int main()
{
freopen("1.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--){
ac.init();
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",s);
ac.insert(s);
}
ac.build();
getchar(); //getchar的意义在于隔绝前面的输入,如果没有的话,gets不能输入的
gets(str);
ac.count(str);
printf("%s",str);
printf("\n");
}
return 0;
}