bzoj5417&&luogu4770你的名字
分析
题目大意:
给定一个模板串
S
S
S,每次给定一个字符串
T
T
T和
l
,
r
l,r
l,r,求
T
T
T中有多少个本质不同的子串无法匹配
S
S
S的子串
S
[
l
⋯
r
]
S[l\cdots r]
S[l⋯r]
首先肯定考虑的是
l
=
1
,
r
=
S
l=1,r=S
l=1,r=S的情况。
分析
T
T
T的每一个前缀
T
[
1
⋯
i
]
T[1\cdots i]
T[1⋯i]
肯定存在一个分界点
x
x
x,使得这个前缀
T
[
1
⋯
i
]
T[1\cdots i]
T[1⋯i]的后缀有
∀
x
≥
j
,
T
[
x
⋯
i
]
\forall x\ge j,T[x\cdots i]
∀x≥j,T[x⋯i]可以匹配
S
S
S
∀
x
<
j
,
T
[
x
⋯
i
]
\forall x < j,T[x\cdots i]
∀x<j,T[x⋯i]无法匹配
S
S
S
并且这个
j
j
j是单调递增的,我们令
l
i
m
i
=
i
−
j
+
1
lim_i=i-j+1
limi=i−j+1。
由于题目中要求的是本质不同的子串,所以我们必须对
T
T
T建立后缀自动机,对于每一个
T
T
T上的节点,假设其
R
i
g
h
t
Right
Right集合中最先出现的位置为
p
o
s
pos
pos,那么这个节点
v
v
v的贡献就是
m
x
[
v
]
−
max
{
m
x
[
f
a
[
v
]
]
,
l
i
m
[
p
o
s
[
v
]
]
}
mx[v]-\max \{mx[fa[v]],lim[pos[v]]\}
mx[v]−max{mx[fa[v]],lim[pos[v]]}
这个式子的意义是,考虑后缀自动机的
p
a
r
e
n
t
parent
parent树等价于把原串的所有前缀逆序插入
T
r
i
e
Trie
Trie后压缩,
m
x
mx
mx就是这个节点到根节点的所代表的字符串长度,那么这个节点就压缩了串
m
x
[
v
]
⋯
m
x
[
f
a
[
v
]
]
mx[v]\cdots mx[fa[v]]
mx[v]⋯mx[fa[v]]中的信息,而
l
i
m
lim
lim是表示从某个位置往前没有贡献的最后一个位置。所以产生贡献的长度就是
m
x
[
v
]
−
max
{
m
x
[
f
a
[
v
]
]
,
l
i
m
[
p
o
s
[
v
]
]
}
mx[v]-\max \{mx[fa[v]],lim[pos[v]]\}
mx[v]−max{mx[fa[v]],lim[pos[v]]}
现在考虑如何求
l
i
m
lim
lim
按顺序考虑
T
T
T的每一个前缀,由于
j
j
j的单调性质,可以采用类似双指针的方式,随着前缀的挪动,去找到合法的
j
j
j。
由于是匹配
S
S
S的子串,所以对
S
S
S另外建立一颗后缀自动机,我们只需要在
S
S
S上进行匹配。如果新走了某字符
c
c
c,如果存在
T
r
a
n
s
(
n
o
w
,
c
)
Trans(now,c)
Trans(now,c),那么就往下走,否则的话就移动
j
j
j,跳
f
a
i
l
fail
fail链即可。
现在考虑加上
l
,
r
l,r
l,r的限制。
实际上等价于当前节点
n
o
w
now
now的
R
i
g
h
t
Right
Right集合内存在某个位置
p
∈
[
l
+
i
−
j
+
1
,
r
]
p\in[l+i-j+1,r]
p∈[l+i−j+1,r]
因为你要从当前位置
p
p
p匹配长度为
i
−
j
+
1
i-j+1
i−j+1的串,也就是
l
i
m
lim
lim
接下来我们只要求查询
S
S
S后缀自动机上某个节点
u
u
u的
R
i
g
h
t
Right
Right集合中是否含有在某个区间内的数。
这个东西采用线段树合并或者主席树维护子树
D
f
s
Dfs
Dfs即可。
因为某个节点的
R
i
g
h
t
Right
Right集合就是其
p
a
r
e
n
t
parent
parent树上儿子的
R
i
g
h
t
Right
Right集合的并。
代码
采用线段树合并
#include<bits/stdc++.h>
#define re(x) std::memset(x, 0, sizeof(x))
const int N = 1e6 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int ps[N], rt[N], c[N], sa[N], lim[N], n;
struct Segment {
int ls[N * 20], rs[N * 20], top;
void Modify(int &p, int L, int R, int x) {
p = ++top; if(L == R) return ; int m = L + R >> 1;
x <= m ? Modify(ls[p], L, m, x) : Modify(rs[p], m + 1, R, x);
}
int Merge(int u, int v) {
if(!u || !v) return u | v;
int np = ++top;
ls[np] = Merge(ls[u], ls[v]);
rs[np] = Merge(rs[u], rs[v]);
return np;
}
bool Que(int p, int L, int R, int st, int ed) {
if(!p || st > ed) return false;
if(L == st && ed == R) return true;
int m = L + R >> 1; bool r = false;
if(st <= m) r |= Que(ls[p], L, m, st, std::min(ed, m));
if(ed > m) r |= Que(rs[p], m + 1, R, std::max(m + 1, st), ed);
return r;
}
}seg;
struct SAM {
int ch[N][26], mx[N], fa[N], last, top;
void Init() {re(ch[1]); last = top = 1;}
int New(int x) {return mx[++top] = x, re(ch[top]), fa[top] = 0, top;}
void Extend(int c, int x, bool e) {
int p = last, np = last = New(mx[p] + 1); if(e) ps[np] = x;
for(;p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) fa[np] = 1;
else {
int q = ch[p][c];
if(mx[q] == mx[p] + 1) fa[np] = q;
else {
int nq = New(mx[p] + 1);
memcpy(ch[nq], ch[q], sizeof(ch[nq]));
fa[nq] = fa[q]; if(e) ps[nq] = x;
fa[q] = fa[np] = nq;
for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
if(!e) seg.Modify(rt[np], 1, n, x);
}
void Work() {
for(int i = 1;i <= top; ++i) ++c[mx[i]];
for(int i = 1;i <= n; ++i) c[i] += c[i - 1];
for(int i = 1;i <= top; ++i) sa[c[mx[i]]--] = i;
for(int i = top; i; --i)
rt[fa[sa[i]]] = seg.Merge(rt[fa[sa[i]]], rt[sa[i]]);
}
}S, T;
void rc(int *s, int &n, SAM &S, bool e) {
S.Init(); char c = getchar(); for(;c < 'a' || c > 'z'; c = getchar()) ; n = 0;
for(;c >= 'a' && c <= 'z'; c = getchar()) s[++n] = c - 'a';
for(int i = 1;i <= n; ++i) S.Extend(s[i], i, e);
}
int s[N];
int main() {
rc(s, n, S, 0); S.Work();
for(int X = ri(), m;X--;) {
rc(s, m, T, 1); int l = ri(), r = ri(), tot = 0;
for(int i = 1, su = 1;i <= m; ++i) {
int c = s[i];
for(;su && !(S.ch[su][c] && seg.Que(rt[S.ch[su][c]], 1, n, l + tot, r));) {
if(!tot) {su = 0; break;}
if(--tot == S.mx[S.fa[su]]) su = S.fa[su];
}
if(!su) su = 1, tot = 0;
else ++tot, su = S.ch[su][c];
lim[i] = tot;
}
long long Ans = 0;
for(int i = 2;i <= T.top; ++i)
Ans += std::max(0, T.mx[i] - std::max(T.mx[T.fa[i]], lim[ps[i]]));
printf("%lld\n", Ans);
}
return 0;
}