bzoj5417&&luogu4770你的名字
分析
题目大意:
给定一个模板串
,每次给定一个字符串
和
,求
中有多少个本质不同的子串无法匹配
的子串
首先肯定考虑的是
的情况。
分析
的每一个前缀
肯定存在一个分界点
,使得这个前缀
的后缀有
可以匹配
无法匹配
并且这个
是单调递增的,我们令
。
由于题目中要求的是本质不同的子串,所以我们必须对
建立后缀自动机,对于每一个
上的节点,假设其
集合中最先出现的位置为
,那么这个节点
的贡献就是
这个式子的意义是,考虑后缀自动机的
树等价于把原串的所有前缀逆序插入
后压缩,
就是这个节点到根节点的所代表的字符串长度,那么这个节点就压缩了串
中的信息,而
是表示从某个位置往前没有贡献的最后一个位置。所以产生贡献的长度就是
现在考虑如何求
按顺序考虑
的每一个前缀,由于
的单调性质,可以采用类似双指针的方式,随着前缀的挪动,去找到合法的
。
由于是匹配
的子串,所以对
另外建立一颗后缀自动机,我们只需要在
上进行匹配。如果新走了某字符
,如果存在
,那么就往下走,否则的话就移动
,跳
链即可。
现在考虑加上
的限制。
实际上等价于当前节点
的
集合内存在某个位置
因为你要从当前位置
匹配长度为
的串,也就是
接下来我们只要求查询
后缀自动机上某个节点
的
集合中是否含有在某个区间内的数。
这个东西采用线段树合并或者主席树维护子树
即可。
因为某个节点的
集合就是其
树上儿子的
集合的并。
代码
采用线段树合并
#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;
}