比赛时队友轻松切掉的水题= =,但我是最近抽时间学了一下AC自动机才理解了此题的正确思路。
个人感觉AC自动机就是一棵升级的 T r i e Trie Trie树,构造的思想又和 K M P KMP KMP雷同,入门还是非常容易的~
思路:
将所有串都丢进AC自动机,每个节点记录一下深度。
每个询问对自动机询问两次,第一次询问进行染色,第二次询问针对染色过的节点更新答案。
代码:
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int A = 1e5 + 10;
const int alp = 26;
char s[A];
int pos[A];
class AC{
public:
int Trie[A][alp],val[A],f[A],col[A],sz,root;
int newnode(){
memset(Trie[sz],0,sizeof(Trie[sz]));
f[sz] = val[sz] = col[sz] = 0;
return sz++;
}
void init(){
sz = 0;root = newnode();
}
void insert(char *s){
int len = strlen(s),u = root;
for(int i=0 ;i<len ;i++){
int id = s[i] - 'a';
if(!Trie[u][id]) Trie[u][id] = newnode();
val[Trie[u][id]] = val[u] + 1;
u = Trie[u][id];
}
}
void build(){
queue<int> que;
que.push(root);
while(!que.empty()){
int u = que.front();que.pop();
for(int i=0 ;i<alp ;i++){
int v = Trie[u][i];
if(!v) Trie[u][i] = Trie[f[u]][i];
else que.push(v);
if(u&&v) f[v] = Trie[f[u]][i];
}
}
}
int query(char* s,int c,bool sta){
int len = strlen(s),u = root,ans = 0;
for(int i=0 ;i<len ;i++){
int id = s[i] - 'a';
u = Trie[u][id];
int tem = u;
while(tem){
if(!sta) col[tem] = c;
else if(col[tem] == c) ans = max(ans,val[tem]);
tem = f[tem];
}
}
return ans;
}
}ac;
int main(){
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
int now = 0;ac.init();
for(int i=1 ;i<=n ;i++){
pos[i] = now;
scanf("%s",s+now);
ac.insert(s+now);
int len = strlen(s+now);
now += len+1;
}
ac.build();
int q;scanf("%d",&q);
for(int i=1;i<=q ;i++){
int x,y;
scanf("%d%d",&x,&y);
ac.query(s+pos[x],i,0);
int ans = ac.query(s+pos[y],i,1);
printf("%d\n",ans);
}
}
return 0;
}