题意:
给n个字符串,和q个询问,每个询问给一个前缀和后缀,问你在这n个字符串中有多少个包含这一对前缀和后缀,前缀后缀不能重叠。
题解:
这题有一个巧妙的办法,用AC自动机去跑。
比如待匹配串是abc,abcd,那么我们将它们转换为abc{abc,abcd{abcd,
为什么用'{'呢,因为ascll表里面{位于z的后面,[位于Z的后面,这样比用'#'更方便。
ps:我一开始溢出了,这vscode居然不给我报错。。。。
然后询问是a c和ab cd ,那么就将询问转换为c{a和cd{ab。
然后将询问插进AC自动机,将每个带匹配串拿去匹配一下,同时注意a 匹配 a a,这里重合了,所以每次匹配要判断长度。
code:
#include<bits/stdc++.h>
using namespace std;
#define N 100003
int n,tot,qq,ans_temp[N],Ans[N],LEN[N];
string T[N];
struct trie{
int son[29],fail,val,id;
}tr[N*51];
queue<int>Q;
void insert(){
string S,B;
int root=0,tot=0;
memset(tr[0].son,0,sizeof(tr[0].son));
tr[0].fail=tr[0].val=tr[0].id=0;
for(int i=1;i<=qq;i++){
ans_temp[i]=i;
Ans[i]=0;
}
for(int i=1;i<=qq;i++){
root=0;
cin>>S>>B;
B=B+'{';
B=B+S;
S=B;
int lens=S.length();
for(int p=0;p<lens;++p){
int k=S[p]-'a';
// cout<<root<<"_"<<k<<endl;
if(!tr[root].son[k]){
memset(tr[tot+1].son,0,sizeof(tr[tot+1].son));
tr[tot+1].fail=tr[tot+1].val=tr[tot+1].id=0;
tr[root].son[k]=++tot;
}
root=tr[root].son[k];
}
if(!tr[root].id)
tr[root].id=i;
else ans_temp[i]=tr[root].id;
LEN[tr[root].id]=lens;
}
}
void makefail(){
while(!Q.empty())Q.pop();
for(int i=0;i<27;i++){
if(tr[0].son[i]){
tr[tr[0].son[i]].fail=0;
Q.push(tr[0].son[i]);
}
}
while(!Q.empty()){
int u=Q.front();
Q.pop();
for(int i=0;i<27;++i){
if(tr[u].son[i]){
tr[tr[u].son[i]].fail=tr[tr[u].fail].son[i];
Q.push(tr[u].son[i]);
}
else
tr[u].son[i]=tr[tr[u].fail].son[i];
}
}
}
void query(string t){
int le=t.length(),u=0,k;
for(int i=0;i<le;i++)
{
k=t[i]-'a';u=tr[u].son[k];
for(int p=u;p;p=tr[p].fail)
{
if(((le-2)>>1)>=LEN[tr[p].id]-1){
Ans[tr[p].id]++;
}
}
}
return ;
}
int main(){
int Ti;
cin>>Ti;
while(Ti--){
cin>>n>>qq;
for(int i=1;i<=n;i++)
cin>>T[i];
insert();
makefail();
for(int i=1;i<=n;i++){
T[i]=T[i]+'{';
T[i]=T[i]+T[i];
query(T[i]);
}
for(int i=1;i<=qq;i++)printf("%d
",Ans[ans_temp[i]]);
}
}