题目大意:
一开始给出字符串S。
Q次询问,每次给出l,r和字符串T。
问T有多少不同子串不是S[l…r]的子串。
∑
∣
T
∣
<
=
1
e
6
,
∣
S
∣
,
∣
T
∣
<
=
5
e
5
\sum|T|<=1e6,|S|,|T|<=5e5
∑∣T∣<=1e6,∣S∣,∣T∣<=5e5
题解:
不同子串一类问题优先考虑SAM。
感觉这题考的更多是对SAM的理解。
首先思考当 l = 1 , r = ∣ S ∣ l=1,r=|S| l=1,r=∣S∣,即送的68分。
有两种做法:
一种是当做广义SAM来做,先把S的SAM建好,然后把T加进去,再暴力回撤。
这个做法无法改成满分的做法,但是很直观。
另一种是先把S和T的SAM建好。
然后考虑把T扔进去S的SAM跑。
假设现在跑完了 T [ 1.. t ] T[1..t] T[1..t],匹配到了点x。
那么显然 T [ 1.. t ] T[1..t] T[1..t]的长度∈[dep[x]+1…t]的后缀是没有在S中出现过的。
然后我们考虑T的SAM的第t个结尾点y。
T [ 1.. t ] T[1..t] T[1..t]的长度为 [ 1.. d e p [ f a [ y ] ] ] [1..dep[fa[y]]] [1..dep[fa[y]]]的后缀也不用我们考虑,因为它们必然也出现在了前面的前缀中,会在前面就被统计到。
这里就考察了一个理解,就是SAM中为什么会分成 y 和 f a [ y ] y和fa[y] y和fa[y]两个点,因为一个y不足以表示 f a [ y ] fa[y] fa[y]和更小长度的后缀,它们在前面就出现过了,所以要区分成两个点。
那么我们对 [ d e p [ x + 1 ] . t ] [dep[x+1].t] [dep[x+1].t]和 [ d e p [ f a [ y ] ] + 1.. t ] [dep[fa[y]]+1..t] [dep[fa[y]]+1..t]取个交就行了,注意这两个dep是不同的,是在两个SAM上的。
然后再思考满分做法。
大概也是在S的SAM上跑,我们要思考的问题是如何在S的SAM上跑出在S[l…r]的SAM上的feel
如果在跑完了 T [ 1.. t ] T[1..t] T[1..t]后,跑到了点x。
如果点x的Right集中没有一个右端点形成的串完全在 [ l . . r ] [l..r] [l..r]中,那么x就可以跳到fa[x],形成的串的长度当然取最小的 d e p [ f a [ x ] ] + 1 dep[fa[x]]+1 dep[fa[x]]+1
那么要维护Right集合中<=r的最大点,用线段树合并+线段树二分即可。
那么就可以维护出 T [ 1.. t ] T[1..t] T[1..t]最长的后缀出现在 S [ l . . r ] S[l..r] S[l..r]中了,剩下的做法和68分一样。
可以看出这道第三题的思维难度没有第二题大,且代码量适中,都是模板,没有什么细节,能否做出取决于在考场上是否能够冷静下来仔细分析。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define pp printf
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
using namespace std;
const int N = 1e6 + 5;
char s[N]; int n, m;
int Q, l, r;
int ed[N];
struct tree {
int l, r, s;
} t[N * 40];
int pl, pr, px, g[N], tt;
void add(int &i, int x, int y) {
if(y < pl || x > pr) return;
if(!i) i = ++ tt;
if(x == y) { t[i].s ++; return;}
int m = x + y >> 1;
add(t[i].l, x, m); add(t[i].r, m + 1, y);
t[i].s = t[t[i].l].s + t[t[i].r].s;
}
void bin(int &i, int x, int y) {
if(!x || !y) { i = x + y; return;}
i = ++ tt;
bin(t[i].l, t[x].l, t[y].l); bin(t[i].r, t[x].r, t[y].r);
t[i].s = t[t[i].l].s + t[t[i].r].s;
}
void ft(int i, int x, int y) {
// pp("%d %d %d %d\n", i, t[i].s, x, y);
if(!i || y < pl || x > pr || !t[i].s) return;
if(x == y) { px = x; return;}
int m = x + y >> 1;
ft(t[i].r, m + 1, y);
if(!px) ft(t[i].l, x, m);
}
struct sam {
int son[N][26], fa[N], dep[N], la, tot;
#define push(v) dep[++ tot] = v
void build() {
fo(j, 0, 25) son[0][j] = 1;
fo(i, 1, tot) memset(son[i], 0, sizeof son[i]);
la = tot = 1;
}
void add(int c) {
int p = la;
push(dep[p] + 1); int np = tot;
for(; p && !son[p][c]; p = fa[p]) son[p][c] = np;
if(!p) fa[np] = 1; else {
int q = son[p][c];
if(dep[p] + 1 < dep[q]) {
push(dep[p] + 1); int nq = tot;
memcpy(son[nq], son[q], sizeof son[q]);
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
for(; son[p][c] == q; p = fa[p]) son[p][c] = nq;
} else fa[np] = q;
}
la = np;
}
int r[N], d[N];
void dg(int x) {
fo(i, 1, d[0]) pp("%c", 'a' + d[i]); pp("\n");
fo(j, 0, 25) if(son[x][j]) {
d[++ d[0]] = j;
dg(son[x][j]);
d[0] --;
}
}
void nb() {
fo(i, 1, tot) r[fa[i]] ++;
fo(i, 1, tot) if(!r[i]) d[++ d[0]] = i;
for(int i = 1; i <= d[0]; i ++) {
int x = d[i];
if(!(-- r[fa[x]])) d[++ d[0]] = fa[x];
if(fa[x]) bin(g[fa[x]], g[fa[x]], g[x]);
}
}
} suf, sb;
ll ans;
int main() {
freopen("name.in", "r", stdin);
freopen("name.out", "w", stdout);
scanf("%s", s + 1); n = strlen(s + 1);
suf.build();
fo(i, 1, n) {
suf.add(s[i] - 'a');
ed[i] = suf.la;
}
// suf.d[0] = 0; suf.dg(1);
fo(i, 1, n) {
pl = pr = i;
add(g[ed[i]], 1, n);
}
suf.nb();
scanf("%d", &Q);
fo(ii, 1, Q) {
scanf("%s", s + 1); m = strlen(s + 1);
scanf("%d %d", &l, &r);
pl = l; pr = r;
ans = 0;
sb.build();
int x = 1, L = 0;
fo(i, 1, m) {
int c = s[i] - 'a';
sb.add(c);
while(x && !suf.son[x][c]) x = suf.fa[x], L = suf.dep[x];
x = suf.son[x][c]; L += x != 1;
while(x > 1) {
px = 0; ft(g[x], 1, n);
if(!px || px - suf.dep[suf.fa[x]] < l) x = suf.fa[x], L = suf.dep[x]; else break;
}
int len = min(suf.dep[x], L);
if(px == 0) len = 0; else {
len = min(len, px - l + 1);
}
int y = sb.la;
int st = sb.dep[sb.fa[y]];
st = max(st, len);
ans += max(0, sb.dep[y] - st);
}
pp("%lld\n", ans);
}
}