题面:luogu4770
题意:给定一个串 S S S。每次询问给出一个字符串 T T T,问 T T T有多少个不同的子串使得其也不是 S [ l … r ] S[l … r] S[l…r]的子串。 ∣ S ∣ ≤ 5 × 1 0 5 |S| \le 5 \times 10^5 ∣S∣≤5×105, q ≤ 1 0 5 q \le 10^5 q≤105, ∑ ∣ T ∣ ≤ 1 0 6 \sum |T| \le 10^6 ∑∣T∣≤106
题解:首先考虑部分分 l = 1 , r = ∣ S ∣ l = 1, r = |S| l=1,r=∣S∣的做法:对 S S S和 T T T建后缀自动机。对 T T T的每个位置求出以这个位置为右端点,向左最多匹配 S S S的长度 m x mx mx。那么对于 T T T后缀自动机每个节点,其贡献的答案为 l e n [ v ] − m a x ( l e n [ l i n k [ v ] ] , m x ) len[v] - max (len[link[v]], mx) len[v]−max(len[link[v]],mx)。
考虑 l , r l, r l,r任意的情况。对于每个 S S S的每个节点开棵线段树维护 e n d p o s endpos endpos。线段树合并。
一样在 S S S的后缀自动机上匹配。现在需要询问后缀自动机上当前节点的所有 e n d p o s endpos endpos能否将该串放入 [ l , r ] [l, r] [l,r]。
即询问 [ l + l e n − 1 , r ] [l + len - 1, r] [l+len−1,r]有没有合法的 e n d p o s endpos endpos。在线段树上查询即可。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
typedef long long ll ;
const int maxn = 1e6 + 10 ;
int n ;
char s[maxn] ;
int mx[maxn], rt[maxn], c[maxn], p[maxn] ;
vector<int> E[maxn] ;
struct segmentTree {
int tot, ch[maxn * 20][2] ;
void insert (int &now, int pos, int l, int r) {
now = ++ tot ;
if (l == r) return ;
int m = (l + r) >> 1 ;
if (pos <= m) insert (ch[now][0], pos, l, m) ;
else insert (ch[now][1], pos, m + 1, r) ;
}
int merge (int x, int y) {
if (!x || !y) return x + y ;
int now = ++ tot ;
ch[now][0] = merge (ch[x][0], ch[y][0]) ;
ch[now][1] = merge (ch[x][1], ch[y][1]) ;
return now ;
}
bool query (int x, int L, int R, int l, int r) {
if (!x) return 0 ;
if (L <= l && R >= r) return 1 ;
int m = (l + r) >> 1, res = 0 ;
if (L <= m) res |= query (ch[x][0], L, R, l, m) ;
if (R > m) res |= query (ch[x][1], L, R, m + 1, r) ;
return res ;
}
}tree ;
struct suffixAutomaton {
int sz, last ;
struct node {
int len, link, pos, nxt[26] ;
}st[maxn] ;
void init () {
for (int i = 1; i <= sz; i ++) {
st[i].len = st[i].link = st[i].pos = 0 ;
memset (st[i].nxt, 0, sizeof st[i].nxt) ;
}
sz = last = 1 ;
}
void insert (int c, int id) {
int cur = ++ sz; st[cur].pos = id ;
st[cur].len = st[last].len + 1 ;
int p = last ;
while (p && !st[p].nxt[c]) {
st[p].nxt[c] = cur; p = st[p].link ;
}
if (!p) {
st[cur].link = 1; last = cur; return ;
}
int q = st[p].nxt[c] ;
if (st[p].len + 1 == st[q].len) {
st[cur].link = q ;
} else {
int clone = ++ sz ;
st[clone] = st[q] ;
st[clone].len = st[p].len + 1 ;
while (p && st[p].nxt[c] == q) {
st[p].nxt[c] = clone; p = st[p].link ;
}
st[cur].link = st[q].link = clone ;
}
last = cur ;
}
}A, B ;
void dfs (int v) {
for (int i = 0; i < E[v].size(); i ++) {
int to = E[v][i] ;
dfs (to) ;
rt[v] = tree.merge (rt[v], rt[to]) ;
}
}
bool check (int u, int len, int l, int r, int c) {
if (l + len > r) return 0 ;
if (!A.st[u].nxt[c]) return 0 ;
u = A.st[u].nxt[c] ;
return tree.query (rt[u], l + len, r, 1, n) ;
}
int main() {
scanf("%s", s + 1) ;
n = strlen (s + 1) ;
A.init () ;
for (int i = 1; i <= n; i ++) {
A.insert (s[i] - 'a', i) ;
tree.insert (rt[A.last], i, 1, n) ;
}
for (int i = 1; i <= A.sz; i ++) E[A.st[i].link].push_back (i) ;
dfs (1) ;
int q, l, r ;
scanf("%d", &q) ;
while (q --) {
scanf("%s", s + 1) ;
int l = read(), r = read() ;
int len = strlen (s + 1) ;
B.init () ;
int u = 1, ln = 0 ;
for (int i = 1; i <= len; i ++) {
while (u && !check (u, ln, l, r, s[i] - 'a')) {
if (-- ln <= A.st[A.st[u].link].len)
u = A.st[u].link ;
}
if (!u) u = 1, ln = 0 ;
else u = A.st[u].nxt[s[i] - 'a'], ln ++ ;
B.insert (s[i] - 'a', i) ;
mx[i] = ln ;
}
ll ans = 0 ;
for (int i = 2; i <= B.sz; i ++) ans += max (0, B.st[i].len - max (mx[B.st[i].pos], B.st[B.st[i].link].len)) ;
printf("%lld\n", ans) ;
}
return 0 ;
}