给出若干仅由小写字母构成的A串、B串,问对于每一个A串,所有的B串在该串中出现的次数。
标程是用AC自动机做的。蒟蒻暂时还不太会AC自动机。
然后看到有用字典树直接做的,就学习了一下。
求B在A中总共出现的次数,其实就是求A的所有子串中含有B的个数。而一个串的所有子串可以表示为它所有后缀的前缀。因此,可以把B[i]插入字典树中,对于每一个A[i],查询它的每一个后缀的所有前缀在字典树中出现了几次,累加起来就是答案。
用C++交RE,用G++就过了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cstring>
#include<vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef __int64 LI;
typedef unsigned __int64 uLI;
typedef unsigned int uI;
typedef double db;
#define maxn 100005
#define inf 0x3f3f3f3f
int n,m;
char A[100001][10005];
char B[100005];
struct Trie{
int tree[100005][26],val[100005],cnt;//结点编号,从1开始
Trie(){
init();
}
void init()
{
cnt=1;
memset(tree,0,sizeof(tree));
}
void insert(char s[]){
int p=0,l=strlen(s);
for(int i=0;i<l;++i){
int a=s[i]-'a';
if(!tree[p][a]){
val[cnt]=0;
tree[p][a]=cnt++;
}
p=tree[p][a];//指向下一结点编号
}
++val[p];
}
int querry(char s[]){
int p=0,l=strlen(s),ans=0;
for(int i=0;i<l;++i)
{
int a=s[i]-'a';
if(!tree[p][a]) return ans;
p=tree[p][a];
ans+=val[p];
}
return ans;
}
}T;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
T.init();
for(int i=0;i<n;++i) scanf("%s",A[i]);
LI ans;
for(int i=0;i<m;++i) {scanf("%s",B);T.insert(B);}
for(int i=0;i<n;++i)
{
ans=0;
int l=strlen(A[i]);
for(int j=0;j<l;++j) ans+=T.querry(A[i]+j);
printf("%I64d\n",ans);
}
}
return 0;
}