给出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 ",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; }