给出n个字符串,再给出一个字符串,把之前出现过的字符串全部变成*
思路:
AC自动机,Trie树上存的值是一个字符串的长度,也就是往前的长度,然后倒着处理一遍。
感想:
第三题AC自动机,本来就是想脱离模板多练练,虽然之前撒比bug错了一大堆,但是收获很多啊。
重要的感想有两个方面:
一:
在我们solve主串的时候:
在通过移动失败指针处理后缀串的时候,在这道题里只要找到一个就行了。
看了网上,主要有两种标记方法(其实类似),其中一种就是通过标记这个位置最长后缀来处理,这样完全可行,然而那个博主的处理方法并不合适,
那个博主的方法是在通过移动失败指针处理后缀串的时候,还在比较取这个位置的后缀串最大,其实理解的话,我们完全不需要比较啊,理由:这个后缀串本身就是你的子串,何必啊???而且在通过移动失败指针处理后缀串的时候第一个即最长。
二:
一开始无脑在线printf,本身就是比较费时的写法,然后就靠评测机抖一抖AC,直接先转变好,一发printf,妥妥的省了一堆时间。
(三:
模板还是网赛用用吧???
//#include <bits/stdc++.h>
#include<iostream>
#include<queue>
#include<string.h>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e6+10;
struct Trie{
int num;
Trie *next[27],*fail;
};
Trie q[N],*root;
int tol;
char word[N],str[N];
//int latersum[N];//第一种方法
int flag[N];//第二种方法
Trie* Creat()
{
Trie *p;
p=&q[tol++];
p->num=0;
p->fail=NULL;
for(int i=0;i<26;i++)
p->next[i]=NULL;
return p;
}
void Insert()
{
Trie* p=root;
int len=strlen(str),index;
for(int i=0;i<len;i++)
{
index=str[i]-'a';
if(p->next[index]==NULL)
p->next[index]=Creat();
p=p->next[index];
}
p->num=len;
}
void Build_Ac()
{
queue<Trie*>que;
que.push(root);
while(!que.empty())
{
Trie *p=que.front();que.pop();
for(int i=0;i<26;i++)
{
if(p->next[i]!=NULL)
{
if(p==root)
p->next[i]->fail=root;
else
{
Trie* temp=p->fail;
while(temp!=NULL)
{
if(temp->next[i]!=NULL){
p->next[i]->fail=temp->next[i];
break;
}
temp=temp->fail;
}
if(temp==NULL)
p->next[i]->fail=root;
}
que.push(p->next[i]);
}
}
}
}
void Query()
{
int len=strlen(word),index;
Trie *p=root;
for(int i=0;i<len;i++)
{
// latersum[i]=0;
flag[i]=0;
if(!((word[i]>='a'&& word[i]<='z')||(word[i]>='A'&&word[i]<='Z')))
continue;
index=(word[i]>='A'&&word[i]<='Z')?(tolower(word[i])-'a'):(word[i]-'a');
while(p->next[index]==NULL && p!=root)
p=p->fail;
p=p->next[index];
if(p==NULL)
p=root;
Trie *temp=p;
while(temp!=root)
{
if(temp->num){
// flag[i]=max(flag[i],temp->num);
flag[i]=temp->num;
// latersum[i+1]--;
// latersum[i-temp->num+1]++;
break;//第一个后缀一定是最长的,不需要在转移到别的fail指针
}
temp=temp->fail;
}
}
// int nn=0;//无脑printf,此代码要看天命AC
// for(int i=0;i<len;i++)
// {
// nn+=latersum[i];
if(nn<=0)
printf("%c",word[i]);
else
// if(nn>0)
// word[i]='*';
// }
// puts("");
int nn=0;
for(int i=len-1;i>=0;i--)
{
nn=max(nn,flag[i]);
if(!nn) continue;
else{
word[i]='*';
nn--;
}
}
printf("%s\n",word);
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
tol=0;
root=Creat();
while(n--)
{
scanf("%s",str);
Insert();
}
getchar();
Build_Ac();
gets(word);
Query();
}
return 0;
}