题目链接
先考虑68分的做法:
求在A串中出现,且在B串中没出现的串的数量。
使用容斥,用A的不同子串数减去A,B的不同公共子串数。
先用双指针,求出A的每个位置开始,在B中最多能向后匹配多远。
然后,问题变为,给你一些区间,问它们的子区间中有多少不同的串。
因为每个串,都是原区间\([l,r]\)中\([l,i]\)的后缀。\((l<=i<=r)\)
而后缀就是在前面去掉一些字符,就是不断走fa。
所以可以建出fa边的树,然后定位出这些位置,求这些点到根的路径的并。
使用树链合并即可,要注意压缩的问题。
100分做法:与68基本相同,就是判断子串\([a,b]\)是否出现多了一个限制(\(l<=a,b<=r\))。
而一个串匹配的位置,就是这个点子树中红点的集合。
红点对应前缀,所以这个限制可以转化为前缀位置的限制。
需要判断一个点的子树中是否有x~y的数。使用DFS序+主席树即可。
代码:
#include <stdio.h> #include <stdlib.h> #define ll long long int fr[2000010],ne[2000010]; int v[2000010],bs = 0; int wl[2000010],wr[2000010],tm = 1; int xl[2000010]; struct SAM { int trs[2000010][26],fa[2000010]; int len[2000010],np,sl; int red[2000010]; SAM() { np = sl = 1; } void clean() { for (int i = 1; i <= sl; i++) { fa[i] = len[i] = 0; red[i] = false; for (int j = 0; j < 26; j++) trs[i][j] = 0; } np = sl = 1; } void insert(char c, int wz) { c -= 'a'; int p = np; np = ++sl; len[np] = len[p] + 1; while (p != 0 && trs[p][c] == 0) { trs[p][c] = np; p = fa[p]; } if (p == 0) fa[np] = 1; else { int q = trs[p][c]; if (len[q] == len[p] + 1) fa[np] = q; else { int nq = ++sl; fa[nq] = fa[q]; fa[q] = fa[np] = nq; len[nq] = len[p] + 1; for (int j = 0; j < 26; j++) trs[nq][j] = trs[q][j]; while (p != 0 && trs[p][c] == q) { trs[p][c] = nq; p = fa[p]; } } } red[np] = wz; } }; void addb(int a, int b) { v[bs] = b; ne[bs] = fr[a]; fr[a] = bs; bs += 1; } SAM S,T; void dfs1(int u) { xl[tm] = u; wl[u] = tm++; for (int i = fr[u]; i != -1; i = ne[i]) dfs1(v[i]); wr[u] = tm; } int up[1000010]; char zf[1000010]; struct SPx { int u,cd; SPx() {} SPx(int U, int Cd) { u = U; cd = Cd; } }; SPx px[1000010]; ll baoli(int s) { for (int i = 1; i <= T.sl; i++) up[i] = 0; for (int i = 0; i < s; i++) { int cd = px[i].cd, u = px[i].u; while (u != 1) { if (up[u] == T.len[u] - T.len[T.fa[u]]) break; if (cd - T.len[T.fa[u]] > up[u]) up[u] = cd - T.len[T.fa[u]]; u = T.fa[u]; cd = T.len[u]; } } ll rtn = 0; for (int i = 2; i <= T.sl; i++) rtn += up[i]; return rtn; } int he[24000010],cl[24000010],cr[24000010],sl = 0; int jianshu(int l, int r) { int rt = sl++; he[rt] = 0; if (l + 1 == r) return rt; int m = (l + r) >> 1; cl[rt] = jianshu(l, m); cr[rt] = jianshu(m, r); return rt; } int xiugai(int i, int l, int r, int j, int x) { int rt = sl++; if (l + 1 == r) { he[rt] = he[i] + x; return rt; } cl[rt] = cl[i]; cr[rt] = cr[i]; int m = (l + r) >> 1; if (j < m) cl[rt] = xiugai(cl[rt], l, m, j, x); else cr[rt] = xiugai(cr[rt], m, r, j, x); he[rt] = he[cl[rt]] + he[cr[rt]]; return rt; } int chaxun(int i, int l, int r, int L, int R) { if (R <= l || r <= L) return 0; if (L <= l && r <= R) return he[i]; int m = (l + r) >> 1; return chaxun(cl[i], l, m, L, R) + chaxun(cr[i], m, r, L, R); } int gen[1000010],n; bool zichuan(int i, int l, int r, int cd) { l = l + cd - 1; if (l > r) return false; int z = chaxun(gen[wr[i] - 1], 1, n + 1, l, r + 1) - chaxun(gen[wl[i] - 1], 1, n + 1, l, r + 1); return z > 0; } int main() { scanf("%s", zf); for (n = 0; zf[n] != 0; n++) S.insert(zf[n], n + 1); for (int i = 1; i <= S.sl; i++) fr[i] = -1; for (int i = 1; i <= S.sl; i++) addb(S.fa[i], i); dfs1(1); gen[0] = jianshu(1, n + 1); for (int i = 1; i < tm; i++) { if (S.red[xl[i]] != 0) gen[i] = xiugai(gen[i - 1], 1, n + 1, S.red[xl[i]], 1); else gen[i] = gen[i - 1]; } int m; scanf("%d", &m); for (int i = 0; i < m; i++) { int l,r,x = 1,y = 1,s = 0; scanf("%s%d%d", zf, &l, &r); T.clean(); for (int j = 0; zf[j] != 0; j++) T.insert(zf[j], 0); for (int j = 0, k = -1, c = 0; zf[j] != 0; j++) { c -= 1; if (x != 1 && k - j == S.len[S.fa[x]]) x = S.fa[x]; if (y != 1 && k - j == T.len[T.fa[y]]) y = T.fa[y]; if (k < j) k = j, c = 0; while (zf[k] != 0 && S.trs[x][zf[k] - 'a'] != 0 && zichuan(S.trs[x][zf[k] - 'a'], l, r, c + 1)) { c += 1; x = S.trs[x][zf[k] - 'a']; y = T.trs[y][zf[k] - 'a']; px[s++] = SPx(y, c); k += 1; } } ll zo = 0; for (int j = 1; j <= T.sl; j++) zo = zo + T.len[j] - T.len[T.fa[j]]; printf("%lld\n", zo - baoli(s)); } return 0; }