题目大意:
给出一些字符串,给出前缀后缀模式询问,问有多少字符串符合该模式
解题思路:
之前一直没头绪,看了看dalao的解法,真是奥妙重重。
对于每个询问,我们将它变成 后缀+分隔符+前缀 的形式,如询问ab cd变为cd{ab。
对于每个原串,我们也变为 原串+分隔符+原串的形式,如abecd变为abecd{abecd。
这时我们若用询问串匹配原串就刚好只能匹配到题目所要求的一段了。
所以处理后直接用询问串建AC自动机即可。
为了避免前后缀重叠的情况,我们记录每个询问串处理后的长度len1和原串处理前的长度len2;
只有len2 < <script type="math/tex" id="MathJax-Element-33"><</script>len1+1的时候可以匹配。
最后每个节点记得加上其fail子树中的答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define ll long long
using namespace std;
int getint()
{
int i=,f=;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')c=getchar(),f=-;
for(;c>='0'&&c<='9';c=getchar())i=(i<<)+(i<<)+c-'0';
return i*f;
}
const int N=,M=;
struct node
{
int son[],cnt,fail,len;
}tr[M];
string s[N],t1,t2,t;
int T,n,m,tot,ans[M],pos[N];
int head,tail,que[M];
int Insert()
{
int len=t.length(),po=;
for(int i=;i<len;i++)
{
if(!tr[po].son[t[i]-'a'])
tr[po].son[t[i]-'a']=++tot,tr[tot].len=tr[po].len+;
po=tr[po].son[t[i]-'a'];
}
tr[po].cnt=;
return po;
}
void build_fail()
{
head=tail=;
que[++tail]=;
while(head<tail)
{
int u=que[++head];
for(int i=;i<;i++)
{
int v=tr[u].fail,w=tr[u].son[i];
while(!tr[v].son[i])v=tr[v].fail;
v=tr[v].son[i];
if(w)tr[w].fail=v,que[++tail]=w;
else tr[u].son[i]=v;
}
}
}
void ac_auto(int id)
{
int len=s[id].length(),po=;
for(int i=;i<len;i++)
{
po=tr[po].son[s[id][i]-'a'];
while(tr[po].len>len/+)po=tr[po].fail;
ans[po]++;
}
}
void solve()
{
for(int i=tail;i;i--)
{
int u=que[i],v=tr[u].fail;
ans[v]+=ans[u];
}
}
int main()
{
//freopen("dictionary.in","r",stdin);
//freopen("dictionary.out","w",stdout);
T=getint();
while(T--)
{
memset(tr,,sizeof(tr));
memset(ans,,sizeof(ans));
tot=;
for(int i=;i<;i++)tr[].son[i]=;
n=getint(),m=getint();
for(int i=;i<=n;i++)
{
cin>>s[i];
s[i]+='{'+s[i];
}
for(int i=;i<=m;i++)
{
cin>>t1>>t2;
t=t2+'{'+t1;
pos[i]=Insert();
}
build_fail();
for(int i=;i<=n;i++)
ac_auto(i);
solve();
for(int i=;i<=m;i++)
cout<<ans[pos[i]]<<'\n';
}
}