题目大意
有
N
个字符串,
N,Q≤5∗105
解题思路
看到这种多串匹配为问题,我们可以考虑用
SAM
来实现。首先,我们对
N
个串
我们对于每个
Si
的后缀,在代表这个字符串的节点上打一个
i
的标记,构完
注意:
1.
Trie
构
SAM
要用
Bfs
来构图复杂度才是对的。
2. 如果不构
Trie
,不能加入完一个串直接把
Last=Root
,要在两个串之间加入一个字符集意外的字符,但是这样
SAM
的点集大小就要乘2(在这题空间限制如果是512M的话就会被卡)。
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 1e6 + 5;
struct Sam {
int Len, Root, Pre, Go[3];
} A[MAXN];
struct Trie {
int Go[2], Root;
} Tri[MAXN];
struct Tree {
int l, r, Sum;
} Tr[MAXN * 20];
struct Query {
int l, r, bel;
Query(int a, int b, int c) {l = a, r = b, bel = c;}
Query() {}
};
vector<Query> Q[MAXN];
int Lst, lst, Sum, num, Cnt, tot, QQ, N, Root, Get, Side, Ans[MAXN], Ord[MAXN];
char S[MAXN];
void Read(int &x) {
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
x = 0;
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
void Sam_Push(int l, int r, int bel, int len) {
if (Get < len) return;
Q[Side].push_back(Query(l, r, bel));
}
void Sam_Go(int c) {
for (; lst != Root && !A[lst].Go[c]; lst = A[lst].Pre, Get = A[lst].Len);
if (A[lst].Go[c]) lst = A[lst].Go[c], Get ++;
Side = lst;
}
void Prepare() {
for (int i = 1; i <= QQ; i ++) {
int l, r;
Read(l), Read(r);
scanf("%s", S + 1);
lst = Root, Get = Side = 0;
int Len = strlen(S + 1);
for (int j = 1; j <= Len; j ++) Sam_Go(S[j] - 'a');
Sam_Push(l, r, i, Len);
}
}
int Sam_Add(int lst, int c, int Rt) {
int np = ++ tot, p = lst;
A[np].Len = A[lst].Len + 1;
A[np].Root = Rt;
for (; p && !A[p].Go[c]; p = A[p].Pre) A[p].Go[c] = np;
if (!p) A[np].Pre = Root; else {
int q = A[p].Go[c];
if (A[q].Len == A[p].Len + 1) A[np].Pre = q; else {
int nq = ++ tot;
A[nq] = A[q], A[nq].Root = 0;
A[nq].Len = A[p].Len + 1;
A[np].Pre = A[q].Pre = nq;
for (; p && A[p].Go[c] == q; p = A[p].Pre) A[p].Go[c] = nq;
}
}
return np;
}
void Sort() {
static int tax[MAXN];
memset(tax, 0, sizeof tax);
for (int i = 1; i <= tot; i ++) tax[A[i].Len] ++;
for (int i = 1; i <= tot; i ++) tax[i] += tax[i - 1];
for (int i = tot; i; i --) Ord[tax[A[i].Len] --] = i;
}
void Tr_Merge(int &Rt, int rt, int ot, int l, int r) {
Rt = ++ Cnt;
if (!rt) {Tr[Rt] = Tr[ot]; return;}
if (!ot) {Tr[Rt] = Tr[rt]; return;}
if (l == r) {
Tr[Rt].Sum = max(Tr[ot].Sum, Tr[rt].Sum);
return;
}
int Mid = (l + r) >> 1;
Tr_Merge(Tr[Rt].l, Tr[rt].l, Tr[ot].l, l, Mid), Tr_Merge(Tr[Rt].r, Tr[rt].r, Tr[ot].r, Mid + 1, r);
Tr[Rt].Sum = Tr[Tr[Rt].l].Sum + Tr[Tr[Rt].r].Sum;
}
void Tr_Add(int &Rt, int rt, int l, int r, int Side) {
Rt = ++ Cnt;
if (rt) Tr[Rt] = Tr[rt];
if (l == r) {
Tr[Rt].Sum = 1;
return;
}
int Mid = (l + r) >> 1;
if (Side <= Mid) Tr_Add(Tr[Rt].l, Tr[Rt].l, l, Mid, Side); else
Tr_Add(Tr[Rt].r, Tr[Rt].r, Mid + 1, r, Side);
Tr[Rt].Sum = Tr[Tr[Rt].l].Sum + Tr[Tr[Rt].r].Sum;
}
void Tr_Query(int Rt, int l, int r, int lx, int rx) {
if (!Rt) return;
if (rx < l || lx > r) return;
if (l >= lx && r <= rx) {
Sum += Tr[Rt].Sum;
return;
}
int Mid = (l + r) >> 1;
Tr_Query(Tr[Rt].l, l, Mid, lx, rx), Tr_Query(Tr[Rt].r, Mid + 1, r, lx, rx);
}
void Solve() {
Sort();
for (int i = tot; i; i --) {
int Now = Ord[i];
Tr_Merge(A[A[Now].Pre].Root, A[Now].Root, A[A[Now].Pre].Root, 1, N);
}
for (int i = 1; i <= tot; i ++) {
for (int j = 0; j < Q[i].size(); j ++) {
int l = Q[i][j].l, r = Q[i][j].r, bel = Q[i][j].bel;
Sum = 0;
Tr_Query(A[i].Root, 1, N, l, r);
Ans[bel] = Sum;
}
}
for (int i = 1; i <= QQ; i ++)
printf("%d\n", Ans[i]);
}
void Trie_Add(int bel, int c) {
if (!Tri[Lst].Go[c]) Tri[Lst].Go[c] = ++ num;
Lst = Tri[Lst].Go[c];
Tr_Add(Tri[Lst].Root, Tri[Lst].Root, 1, N, bel);
}
void Sam_Build() {
static int D[MAXN][2], top = 1;
D[1][0] = 1, D[1][1] = 1;
for (int i = 1; i <= top; i ++) {
for (int j = 0; j < 2; j ++) {
if (!Tri[D[i][1]].Go[j]) continue;
int v = Tri[D[i][1]].Go[j];
D[++ top][0] = Sam_Add(D[i][0], j, Tri[v].Root);
D[top][1] = v;
}
}
}
int main() {
scanf("%d%d", &N, &QQ);
Root = tot = lst = Lst = num = 1;
for (int i = 1; i <= N; i ++) {
scanf("\n%s", S + 1);
int Len = strlen(S + 1);
Lst = 1;
for (int j = 1; j <= Len; j ++) Trie_Add(i, S[j] - 'a');
}
Sam_Build();
Prepare();
Solve();
}