Gym 101431B Vera and Banquet (字尾陣列)
阿新 • • 發佈:2018-11-10
一個環形的字串,求本質不同子串數目,順逆時針皆可。
順時針直接複製一遍,統計長度不超過n的種類;逆時針就翻轉過來再複製一遍,統計長度不超過n的種類。因此搞到一起就是把串複製一遍再對稱過去,中間加一個特殊字元,統計長度不超過n的種類再減去含有特殊字元的種類。
不超過n的種類顯然是min(len-sa[i]+1, n),含有特殊字元的種類按長度分類總共是1+2+……+n = n*(n+1)/2。包含特殊子串的種類由於沒有重複,可以直接算就是min(hei[i], n)。
#include <cstdio> #include <cmath> #include <cstring> #include <vector> #include <queue> #include <map> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 200050; const double eps = 1e-8; int len, n; char s[maxn]; int r[maxn], rk[maxn], sa[maxn]; int t1[maxn], t2[maxn], c[maxn], hei[maxn]; bool cmp(int *r, int a, int b, int l) { return r[a] == r[b] && r[a + l] == r[b + l]; } void get_sa(int str[maxn], int n, int m) { int p, *x = t1, *y = t2; for(int i = 0;i < m;i++) c[i] = 0; for(int i = 0;i < n;i++) c[x[i]=str[i]]++; for(int i = 1;i < m;i++) c[i] += c[i-1]; for(int i = n-1;i >= 0;i--) sa[--c[x[i]]] = i; for(int j = 1;j <= n;j <<= 1) { p = 0; for(int i = n-j;i < n;i++) y[p++] = i; for(int i = 0;i < n;i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(int i = 0;i < m;i++) c[i] = 0; for(int i = 0;i < n;i++) c[x[y[i]]]++; for(int i = 1;i < m;i++) c[i] += c[i-1]; for(int i = n-1;i >= 0;i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1, x[sa[0]] = 0; for(int i = 1;i < n;i++) x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++; if(p >= n) break; m = p; } } void get_height() { int k = 0; for(int i = 0;i <= len;i++) rk[sa[i]] = i; for(int i = 0;i < len;i++) { if(k) k--; int j = sa[rk[i]-1]; while(r[i+k] == r[j+k]) k++; hei[rk[i]] = k; } } int main() { scanf("%d", &n); memset(s, 0, sizeof(s)); scanf("%s", s); for(int i = 0;i < n;i++) s[i+n] = s[i]; len = n << 1; s[len++] = '$'; for(int i = (n<<1)-1;i >= 0;i--) s[len++] = s[i]; for(int i = 0;i <= len;i++) r[i] = s[i]; get_sa(r, len + 1, 128); get_height(); ll ans = 0; for(int i = 0;i < len;i++) ans += (min(len - sa[i] + 1, n) - min(hei[i+1], n)); ans -= 1LL*n*(n+1)/2; printf("%I64d\n", ans); return 0; }