Address
Solution…
68pts: l = 1 , r = ∣ S ∣ l=1,r=|S| l=1,r=∣S∣
建议先做:[BZOJ4566][Haoi2016]找相同字符
询问前先建出
S
S
S 的后缀自动机,然后求出
T
T
T 的每个前缀
T
[
1...
i
]
T[1...i]
T[1...i] 的后缀最多能在
S
S
S 中匹配多长。也就是说,我们需要对于每个
i
i
i 求出一个最大的
m
x
[
i
]
=
j
mx[i]=j
mx[i]=j ,使得
T
[
i
−
j
+
1...
i
]
T[i-j+1...i]
T[i−j+1...i] 在
S
S
S 中出现过。那么显然这样对答案的贡献为
i
−
j
i-j
i−j 。
j
j
j 可以用在后缀自动机上走转移边和 Parent 边得到,类似于求最长公共子串的方法。
但我们要统计的是
T
T
T 本质不同的子串,所以我们想办法让已经出现过的子串不被统计。所以建出
T
T
T 的后缀自动机。同样地,可以「自己和自己匹配」,求出一个最大的
n
x
t
[
i
]
=
j
nxt[i]=j
nxt[i]=j 满足
T
[
i
−
j
+
1...
i
]
T[i-j+1...i]
T[i−j+1...i] 是
T
[
1...
i
−
1
]
T[1...i-1]
T[1...i−1] 的子串,那么这样左端点在
[
n
x
t
[
i
]
,
i
]
[nxt[i],i]
[nxt[i],i] 而右端点为
i
i
i 的子串不会被再次统计。
那么这次询问的答案为:
∑
i
=
1
∣
T
∣
{
i
−
max
(
n
x
t
[
i
]
,
m
x
[
i
]
)
}
\sum_{i=1}^{|T|}\{i-\max(nxt[i],mx[i])\}
i=1∑∣T∣{i−max(nxt[i],mx[i])}
复杂度
O
(
∣
S
∣
+
∑
∣
T
∣
)
O(|S|+\sum|T|)
O(∣S∣+∑∣T∣) 。期望得分
68
68
68 分。
100pts: 1 ≤ l ≤ r ≤ ∣ S ∣ 1\le l\le r\le |S| 1≤l≤r≤∣S∣
求
n
x
t
[
i
]
nxt[i]
nxt[i] 还是一样。而关键在于求
m
x
[
i
]
mx[i]
mx[i] 。
假设我们已经求得了
m
x
[
i
−
1
]
mx[i-1]
mx[i−1] 并且走到了
S
S
S 的后缀自动机上的节点
u
u
u 。
先考虑一个小问题:
如何判断状态
u
u
u (不是初始状态)所能够表示出的所有长度为
k
k
k 的子串中,是否存在一个出现在
S
[
l
.
.
.
r
]
S[l...r]
S[l...r] 内。
问题等价于求
u
u
u 的
R
i
g
h
t
Right
Right 集合中是否存在一个数在
[
l
+
k
−
1
,
r
]
[l+k-1,r]
[l+k−1,r] 范围内。
我们知道,如果在构建
S
S
S 的过程中将拆解出的所有点设为白点,其他点(初始状态除外)为黑点,那么点
u
u
u 的
R
i
g
h
t
Right
Right 集合就是
u
u
u 的 Parent 子树内所有黑点。
于是转化为求
u
u
u 的 Parent 子树中是否存在一个黑点的
M
a
x
l
Maxl
Maxl 在
[
l
+
k
−
1
,
r
]
[l+k-1,r]
[l+k−1,r] 范围内。
求出 Parent 树的 DFS 序列之后,可以用主席树求得。
回到原问题。考虑如何判断状态
u
u
u 表示出的所有
S
S
S 的子串是否存在一个子串包含于
[
l
,
r
]
[l,r]
[l,r] 。如果没有则需要把
u
u
u 往 Parent 跳直到存在或者跳回初始状态为止。
易得,我们要求的是
u
u
u 的 Parent 子树内,
M
a
x
l
Maxl
Maxl 在
[
l
,
r
]
[l,r]
[l,r] 范围内并且最大的值。
仍然可以使用主席树查这个值。
这样我们就能得到状态
u
u
u 在
S
[
l
.
.
.
r
]
S[l...r]
S[l...r] 内能够匹配的最长子串。
(注:黑点的
M
a
x
l
Maxl
Maxl 的另一个含义是:这个点表示出的子串之一为
S
S
S 的长度为
M
a
x
l
Maxl
Maxl 的前缀。故
u
u
u 的
R
i
g
h
t
Right
Right 集合实际上是
u
u
u 的 Parent 子树内所有黑点的
M
a
x
l
Maxl
Maxl 组成的集合)
具体实现:求得
m
x
[
i
−
1
]
mx[i-1]
mx[i−1] 以及到达的
S
S
S 中的点
u
u
u ,设
c
=
T
[
i
]
c=T[i]
c=T[i] 。
如果以下两条件都满足:
(1)
u
u
u 有字符
c
c
c 的转移边。设
u
u
u 通过字符
c
c
c 转到
v
v
v 。
(2)
v
v
v 在
S
[
l
.
.
.
r
]
S[l...r]
S[l...r] 内能匹配到子串,设最长长度为
l
l
l 。
就将
u
u
u 通过字符
c
c
c 边转移,并把
m
x
[
i
]
mx[i]
mx[i] 设成
l
l
l 。
否则将
u
u
u 跳到 Parent 继续判断。
裆燃,如果
u
u
u 跳到了根就直接退出。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define SAM_AS(i, z) for (; i && z; i = AS[i].fa)
#define SAM_AT(i, z) for (; i && z; i = AT[i].fa)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
template <class T>
T Max(T a, T b) {return a > b ? a : b;}
template <class T>
T Min(T a, T b) {return a < b ? a : b;}
typedef long long ll;
const int N = 5e5 + 5, M = N << 1, L = 3e7 + 5;
int n, m, q, lst, ToT, QAQ, w[N], tmp[M], ecnt, nxt[M], adj[M], go[M],
rt[M], QwQ, dfn[M], sze[M], TAT, a[M];
char S[N], T[N];
void add_edge(int u, int v)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
struct node
{
int fa, go[26], maxl, ri;
void init()
{
fa = maxl = ri = 0;
memset(go, 0, sizeof(go));
}
} AS[M], AT[M];
struct seg
{
int lc, rc, sum;
} Tr[L];
void ins(int y, int &x, int l, int r, int p)
{
Tr[x = ++TAT] = Tr[y]; Tr[x].sum++;
if (l == r) return;
int mid = l + r >> 1;
if (p <= mid) ins(Tr[y].lc, Tr[x].lc, l, mid, p);
else ins(Tr[y].rc, Tr[x].rc, mid + 1, r, p);
}
int querysum(int y, int x, int l, int r, int up)
{
if (l == r) return Tr[x].sum - Tr[y].sum;
int mid = l + r >> 1;
if (up <= mid) return querysum(Tr[y].lc, Tr[x].lc, l, mid, up);
else return Tr[Tr[x].lc].sum - Tr[Tr[y].lc].sum
+ querysum(Tr[y].rc, Tr[x].rc, mid + 1, r, up);
}
int squery(int y, int x, int l, int r, int sum)
{
if (l == r) return l;
int mid = l + r >> 1, delta = Tr[Tr[x].lc].sum - Tr[Tr[y].lc].sum;
if (sum <= delta) return squery(Tr[y].lc, Tr[x].lc, l, mid, sum);
else return squery(Tr[y].rc, Tr[x].rc, mid + 1, r, sum - delta);
}
int querypre(int l, int r, int tl, int tr)
{
int tmp = querysum(rt[l - 1], rt[r], 0, n, tr);
if (!tmp) return 0;
int rev = squery(rt[l - 1], rt[r], 0, n, tmp);
return tl <= rev ? rev : 0;
}
void extendS(int x)
{
int i = lst, c = S[x] - 'a';
AS[lst = ++ToT].init();
AS[lst].ri = AS[lst].maxl = x;
SAM_AS(i, !AS[i].go[c]) AS[i].go[c] = lst;
if (!i) return (void) (AS[lst].fa = 1);
int j = AS[i].go[c];
if (AS[i].maxl + 1 == AS[j].maxl)
return (void) (AS[lst].fa = j);
int p; AS[p = ++ToT] = AS[j]; AS[p].ri = 0;
AS[lst].fa = AS[j].fa = p;
AS[p].maxl = AS[i].maxl + 1;
SAM_AS(i, AS[i].go[c] == j) AS[i].go[c] = p;
}
void extendT(int x)
{
int i = lst, c = T[x] - 'a';
AT[lst = ++QAQ].init(); AT[lst].maxl = x;
SAM_AT(i, !AT[i].go[c]) AT[i].go[c] = lst;
if (!i) return (void) (AT[lst].fa = 1);
int j = AT[i].go[c];
if (AT[i].maxl + 1 == AT[j].maxl)
return (void) (AT[lst].fa = j);
int p; AT[p = ++QAQ] = AT[j]; AT[p].ri = 0;
AT[lst].fa = AT[j].fa = p;
AT[p].maxl = AT[i].maxl + 1;
SAM_AT(i, AT[i].go[c] == j) AT[i].go[c] = p;
}
void dfs(int u)
{
a[dfn[u] = ++QwQ] = u; sze[u] = 1;
Edge(u) dfs(v), sze[u] += sze[v];
}
void calc_right()
{
int i;
For (i, 2, ToT) add_edge(AS[i].fa, i);
dfs(1);
For (i, 1, ToT) ins(rt[i - 1], rt[i], 0, n, AS[a[i]].ri);
}
int main()
{
AS[lst = ToT = 1].init();
int i, l, r;
scanf("%s", S + 1);
n = strlen(S + 1);
For (i, 1, n) extendS(i);
calc_right();
q = read();
while (q--)
{
scanf("%s", T + 1); l = read(); r = read();
AT[lst = QAQ = 1].init();
m = strlen(T + 1);
ll res = 0;
int u = 1, v = 1, len = 0, dln = 0;
For (i, 1, m)
{
int w, c = T[i] - 'a', sp;
while (v > 1 && !AT[v].go[c])
v = AT[v].fa, dln = AT[v].maxl;
if (AT[v].go[c]) v = AT[v].go[c], dln++;
while (u > 1 && !AS[u].go[c])
u = AS[u].fa, len = AS[u].maxl;
while (u > 1)
{
int v = AS[u].go[c];
int minl = AS[AS[v].fa].maxl + 1, maxl = AS[v].maxl;
sp = querypre(dfn[v], dfn[v] + sze[v] - 1, l, r);
if (!sp) u = AS[u].fa, len = AS[u].maxl;
else
{
int tlen = Min(sp - l + 1, maxl);
if (tlen < minl) u = AS[u].fa, len = AS[u].maxl;
else {len = Min(len, tlen - 1); break;}
}
}
if ((w = AS[u].go[c]) &&
(sp = querypre(dfn[w], dfn[w] + sze[w] - 1, l, r))
&& Min(sp - l + 1, AS[w].maxl) > AS[AS[w].fa].maxl)
u = AS[u].go[c], len++;
res += i - Max(len, dln);
extendT(i);
}
printf("%lld\n", res);
}
return 0;
}