【YBT2022寒假Day1 C】相似子串(SA)(RMQ)(LCP)
阿新 • • 發佈:2022-02-06
給你一個數字串,然後每次問你一個子串,問這個數字串中有多少個子串(包括自己)跟它相似。 定義相似是長度相同,且陣列可以通過把數字集體置換成另一種形式得到另一個數組。
,但是由於只有 \(0\sim 9\) 所以至多變 \(10\) 個地方。
所以我們考慮暴力把這些段找到分開來,一個一個處理。
相似子串
題目連結:YBT2022寒假Day1 C
題目大意
給你一個數字串,然後每次問你一個子串,問這個數字串中有多少個子串(包括自己)跟它相似。
定義相似是長度相同,且陣列可以通過把數字集體置換成另一種形式得到另一個數組。
思路
考慮把這個相似的屬性轉化一下,把相等的顏色要求去掉:
可以從原數字構造新數字串 \(s\),構造方法為如果這個數字第一次出現就是 \(0\),否則就是它跟上個這個數字距離的位置。
那我們的問題就變成了看詢問串的 \(s\) 串跟多少個字尾的 \(s\) 串 LCP 大於等於詢問串的大小。
但是你會發現有點問題,就是你擷取一個子串的話直接用原串的子串會有一些地方變成 \(0\)
所以我們考慮暴力把這些段找到分開來,一個一個處理。
那思路大概就有了,首先把原串搞字尾排序,求出 height 陣列,然後把每個字尾試著分割記錄下來。
然後由於我們最後也要 LCP,所以我們也要給所有分割好的字尾串再排一次序。(大概就是一個區間一個區間的比較,相同就看下一個,不同就比較那裡面的)
然後我們就可以用最後排序的 height 陣列二分左右的量求出答案了。
emmm具體細節很多可以看看程式碼來理解。
程式碼
#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n, q, l, r, s[100001], nxt[100001][11]; int a[100001], lst, ban[11], pl[100001], abab[100001]; int sa[100001], xx[100001], yy[100001], fir[100001], log2_[100001]; int height[100001][21], rnk[100001], tong[100001], kind, ynum; void Sort(int m, int *x, int *y) { for (int i = 1; i <= m; i++) tong[i] = 0; for (int i = 1; i <= n; i++) tong[x[i]]++; for (int i = 2; i <= m; i++) tong[i] += tong[i - 1]; for (int i = n; i >= 1; i--) sa[tong[fir[i]]--] = y[i]; } void SA(int *r, int *sa, int n, int m) { int *x = xx, *y = yy; for (int i = 1; i <= n; i++) x[i] = r[i] + 1; for (int i = 1; i <= n; i++) y[i] = i; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; Sort(m, x, y); for (int j = 1; j < n; j <<= 1) { ynum = 0; for (int i = n - j + 1; i <= n; i++) y[++ynum] = i; for (int i = 1; i <= n; i++) if (sa[i] > j) y[++ynum] = sa[i] - j; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; Sort(m, x, y); swap(x, y); kind = 1; x[sa[1]] = 1; for (int i = 2; i <= n; i++) if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = kind; else x[sa[i]] = ++kind; if (kind == n) break; m = kind; } } void get_height(int *r, int *sa, int n) { int k = 0, j; for (int i = 1; i <= n; i++) rnk[sa[i]] = i; for (int i = 1; i <= n; i++) { if (k) k--; j = sa[rnk[i] - 1]; while (r[i + k] == r[j + k] && i + k <= n && j + k <= n) k++; height[rnk[i]][0] = k; } } void get_RMQ(int n) { log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1; for (int i = 1; i <= 20; i++) for (int j = 1; j + (1 << i) - 1 <= n; j++) height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]); } int RMQ1(int l, int r) { if (!l || !r) return 0; if (l == r) return n - l + 1; l = rnk[l]; r = rnk[r]; if (l > r) swap(l, r); l++; int k = log2_[r - l + 1]; return min(height[l][k], height[r - (1 << k) + 1][k]); } int RMQ2(int l, int r) { l++; int k = log2_[r - l + 1]; return min(height[l][k], height[r - (1 << k) + 1][k]); } struct node { int l, r; }; struct fgd { vector <node> a; int id; }nt[100001]; int cp(node x, node y) { if (!x.l && !y.l) return 2; if (!x.l) return 1; if (!y.l) return 0; int len = RMQ1(x.l, y.l); if (len >= x.r - x.l + 1 || len >= y.r - y.l + 1) { if (x.r - x.l + 1 == y.r - y.l + 1) return 2; return x.r - x.l + 1 < y.r - y.l + 1; } return s[x.l + len] < s[y.l + len]; } bool cmp(fgd x, fgd y) { int now = 0; while (1) { if (now >= x.a.size()) return 0; if (now >= y.a.size()) return 1; int op = cp(x.a[now], y.a[now]); if (op != 2) return op; now++; } } int LCP(fgd x, fgd y) { int re = 0, now = 0; while (now < x.a.size() && now < y.a.size() && cp(x.a[now], y.a[now]) == 2) re += x.a[now].r - x.a[now].l + 1, now++; if (now < x.a.size() && now < y.a.size()) re += min(RMQ1(x.a[now].l, y.a[now].l), min(x.a[now].r - x.a[now].l + 1, y.a[now].r - y.a[now].l + 1)); return re; } int main() { // freopen("similar.in", "r", stdin); // freopen("similar.out", "w", stdout); scanf("%d %d", &n, &q); for (int i = 1; i <= n; i++) { scanf("%1d", &a[i]); if (!ban[a[i]]) s[i] = 0; else s[i] = i - ban[a[i]]; ban[a[i]] = i; } nxt[n][a[n]] = n; for (int i = n - 1; i >= 1; i--) { memcpy(nxt[i], nxt[i + 1], sizeof(nxt[i])); nxt[i][a[i]] = i; }//SA SA(s, sa, n, n); get_height(s, sa, n); get_RMQ(n); for (int i = 1; i <= n; i++) {//分成若干段 nt[i].id = i; memcpy(ban, nxt[i], sizeof(ban)); sort(ban, ban + 9 + 1); int now = i; for (int j = 0; j <= 9; j++) { if (!ban[j]) continue; if (ban[j] > now) nt[i].a.push_back((node){now, ban[j] - 1}); nt[i].a.push_back((node){0, 0}); now = ban[j] + 1; } if (now <= n) nt[i].a.push_back((node){now, n}); } sort(nt + 1, nt + n + 1, cmp);//排序 for (int i = 1; i <= n; i++) pl[nt[i].id] = i; for (int i = 2; i <= n; i++)//用前面的 LCP 來搞新的 height 陣列 abab[i] = LCP(nt[i - 1], nt[i]); for (int i = 2; i <= n; i++) height[i][0] = abab[i]; get_RMQ(n); while (q--) { scanf("%d %d", &l, &r); l ^= lst; r ^= lst; int x = pl[l], sz = r - l + 1, ans = 1;//height兩邊分別二分 int L = x + 1, R = n, re = x; while (L <= R) { int mid = (L + R) >> 1; if (RMQ2(x, mid) >= sz) re = mid, L = mid + 1; else R = mid - 1; } ans += re - x; L = 1; R = x - 1; re = x; while (L <= R) { int mid = (L + R) >> 1; if (RMQ2(mid, x) >= sz) re = mid, R = mid - 1; else L = mid + 1; } ans += x - re; lst = ans; printf("%d\n", lst); } return 0; }