因为机房里的小伙伴都在看《你的名字。》而我不想看
所以来写了这道题...
给一个 $S$ 串,$q$ 次询问,每次一个 $T$ 串,问 $T$ 有多少没在 $S[l,r]$ 中以子串形式出现过的本质不同的子串
$|S|,q leq 5e5,sum |T| leq 5e5$
sol:
容斥一下就变成了 $T$ 与 $S[l,r]$ 有多少本质不同的公共子串
首先把 $T$ 串在 $S$ 串上跑,跑不动就跳 $parent$,这样可以预处理出 $T$ 的每一个前缀能在 $S[l,r]$ 中匹配的最长后缀长度 $len_i$,具体就用可持久化线段树合并维护每个点的 $right$ 集合,然后每次维护一个 $now$,查这个点的 $right$ 集合里有没有 $[l+now-1,r]$ 即可,这样做的话发现跑的节点和这个 $now$ 好像构成了一个双指针的样子,所以复杂度是对的
然后在 $T$ 串的后缀自动机上按每个状态记录答案即可
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar())if(ch == '-') f = -f; for(; isdigit(ch); ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 2e6 + 10; int n, q; char s[maxn]; int root[maxn], ToT; struct Node { int val; int ls, rs; } st[maxn << 6]; inline void Insert(int &x, int l, int r, int pos) { if(!x) x = ++ToT; if(l == r) return (void) (st[x].val++); int mid = (l + r) >> 1; (pos <= mid) ? Insert(st[x].ls, l, mid, pos) : Insert(st[x].rs, mid+1, r, pos); return (void) (st[x].val = st[st[x].ls].val + st[st[x].rs].val); } inline int query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return st[x].val; int mid = (l + r) >> 1, ans = 0; if(L <= mid) ans += query(st[x].ls, l, mid, L, R); if(R > mid) ans += query(st[x].rs, mid+1, r, L, R); return ans; } inline int merge(int x, int y, int l, int r) { if(!x || !y) return x + y; int z = ++ToT; if(l == r) st[z].val = st[x].val + st[y].val; else { int mid = (l + r) >> 1; st[z].ls = merge(st[x].ls, st[y].ls, l, mid); st[z].rs = merge(st[x].rs, st[y].rs, mid+1, r); st[z].val = st[st[z].ls].val + st[st[z].rs].val; } return z; } vector<int> G[maxn]; int ans[maxn]; namespace SAM1 { int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int rot, last, dfn; int tr[maxn][26], fa[maxn], mxlen[maxn]; void extend(int c, int id) { int p = last, np = last = ++dfn; mxlen[np] = mxlen[p] + 1; Insert(root[np], 1, n, id); for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np; if(!p) fa[np] = rot; else { int q = tr[p][c]; if(mxlen[q] == mxlen[p] + 1) fa[np] = q; else { int nq = ++dfn; mxlen[nq] = mxlen[p] + 1; memcpy(tr[nq], tr[q], sizeof(tr[nq])); fa[nq] = fa[q]; fa[q] = fa[np] = nq; for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq; } } } void build() { rep(i, 2, dfn) { add(fa[i], i); //cout << i << " " << fa[i] << endl; } } void dfs(int x) { for(int i=first[x];i;i=nx[i]) dfs(to[i]), root[x] = merge(root[x], root[to[i]], 1, n); } void Query(char *s, int l, int r) { int len = strlen(s + 1); int p = rot, now = 0; for(int i = 1; i <= len; i++){ int c = s[i] - 'a'; for(; p && !tr[p][c]; p = fa[p], now = mxlen[p]); if(!p) { p = rot, now = 0; continue; }; p = tr[p][c], now++; while(p > 1) { if(query(root[p], 1, n, l + now - 1, r)) break; now--; if(now == mxlen[fa[p]]) p = fa[p]; } if(p == rot) continue; for(int j = 0; j < (int) G[i].size(); j++) { ans[G[i][j]] = max(ans[G[i][j]], now); //cout << now << endl; } } } } // namespace SAM1 namespace SAM2 { int rot, last, dfn; int tr[maxn][26], fa[maxn], mxlen[maxn]; void init() { rep(i, 1, dfn) { ans[i] = fa[i] = mxlen[i] = 0; memset(tr[i], 0, sizeof(tr[i])); } rot = last = dfn = 1; } void extend(int c, int id) { int p = last, np = last = ++dfn; mxlen[np] = mxlen[p] + 1; G[id].push_back(np); for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np; if(!p) fa[np] = rot; else { int q = tr[p][c]; if(mxlen[q] == mxlen[p] + 1) fa[np] = q; else { int nq = ++dfn; G[id].push_back(nq); mxlen[nq] = mxlen[p] + 1; fa[nq] = fa[q]; fa[q] = fa[np] = nq; memcpy(tr[nq], tr[q], sizeof(tr[q])); for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq; } } } LL Query() { LL ans1 = 0, ans2 = 0; rep(i, 1, dfn) { if(ans[i] > mxlen[fa[i]]) ans2 += min(ans[i], mxlen[i]) - mxlen[fa[i]]; ans1 += mxlen[i] - mxlen[fa[i]]; // cout << ans[i] << endl; //cout << ans1 << " " << ans2 << endl; } return ans1 - ans2; } } // namespace SAM2 int main() { SAM1::rot = SAM1::last = ++SAM1::dfn; scanf("%s", s + 1); n = strlen(s + 1); rep(i, 1, n) SAM1::extend(s[i] - 'a', i); SAM1::build(); SAM1::dfs(1); q = read(); while(q--) { scanf("%s", s + 1); int l = read(), r = read(); int len = strlen(s + 1); rep(i, 1, len) G[i].clear(); SAM2::init(); rep(i, 1, len) SAM2::extend(s[i] - 'a', i); SAM1::Query(s, l, r); printf("%lld ", SAM2::Query()); } }