思路
我们把每一个后缀+前缀以后缀+任意不是a~z的字符+前缀建立AC自动机,再把字典中的字符串也以一样的形式跑一边AC自动机,求最后每一个后缀加前缀对应的节点出现的次数就可以了
代码(借鉴)
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 2000010;
int pos[MAXN];
struct Trie{
int next[MAXN][27], fail[MAXN], LEN[MAXN], ans[MAXN];
int root, L;
int insert(char buf[])
{
int len = strlen(buf), now = 0;
for(int i = 0; i < len; i++)
{
if(next[now][buf[i] - 'a'] == 0)
{
next[now][buf[i] - 'a'] = ++L;
LEN[L] = i + 1;
}
now = next[now][buf[i] - 'a'];
}
return now;
}
void init()//不要忘记初始化
{
memset(next, 0, sizeof(int) * (L + 1) * 27);
memset(fail, 0, sizeof(int) * (L + 1));
memset(LEN, 0, sizeof(int) * (L + 1));
memset(ans, 0, sizeof(int) * (L + 1));
root = L = 0;
}
void build()
{
queue<int>q;
fail[root] = root;
for(int i = 0; i < 27; i++)
if(next[root][i] == 0)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
q.push(next[root][i]);
}
while(!q.empty())
{
int now = q.front(); q.pop();
for(int i = 0; i < 27; i++)
if(next[now][i] == 0)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
q.push(next[now][i]);
}
}
}
void query(char buf[], int len)
{
int now = root;
for(int i = 0; buf[i]; i++)
{
now = next[now][buf[i] - 'a'];
int tmp = now;
while(tmp != root)
{
if(LEN[tmp] <= len) ans[tmp]++;
tmp = fail[tmp];
}
}
}
}AC;
char str[MAXN];
char *s[100010];
int len[100010];
char s1[100010], s2[100010];
int main()
{
int T, n, q;
scanf("%d", &T);
while(T--)
{
AC.init();
scanf("%d %d", &n, &q);
int cnt = 0;
for(int i = 0; i < n; i++)
{
s[i] = str + cnt;
scanf("%s", s[i]);
len[i] = strlen(s[i]) + 1;
cnt += len[i];
strcpy(str + cnt, s[i]);
str[cnt - 1] = 'z' + 1;
cnt += len[i];
}
for(int i = 0; i < q; i++)
{
s1[0] = 'z' + 1;
scanf("%s%s", s1 + 1, s2);
strcat(s2, s1);
pos[i] = AC.insert(s2);
}
AC.build();
for(int i = 0; i < n; i++)
AC.query(s[i], len[i]);
for(int i = 0; i < q; i++)
printf("%d\n", AC.ans[pos[i]]);
}
return 0;
}