BZOJ3879: SvT【後綴數組+單調棧】

Description

(我並不想告訴你題目名字是什麼鬼)c++

有一個長度爲n的僅包含小寫字母的字符串S,下標範圍爲[1,n].數組

如今有若干組詢問,對於每個詢問,咱們給出若干個後綴(以其在S中出現的起始位置來表示),求這些後綴兩兩之間的LCP(LongestCommonPrefix)的長度之和.一對後綴之間的LCP長度僅統計一遍測試

Input

第一行兩個正整數n,m,分別表示S的長度以及詢問的次數.ui

接下來一行有一個字符串S.spa

接下來有m組詢問,對於每一組詢問,均按照如下格式在一行內給出:code

首先是一個整數t,表示共有多少個後綴.接下來t個整數分別表示t個後綴在字符串S中的出現位置排序

Output

對於每一組詢問,輸出一行一個整數,表示該組詢問的答案.因爲答案可能很大,僅須要輸出這個答案對於23333333333333333(一個巨大的質數)取模的餘數.ip

Sample Input

7 3字符串

popoqqqinput

1 4

2 3 5

4 1 2 5 6

Sample Output

0

0

2

### Hint
樣例解釋:
對於詢問一,只有一個後綴」oqqq」,所以答案爲0.
對於詢問二,有兩個後綴」poqqq」以及」qqq」,兩個後綴之間的LCP爲0,所以答案爲0.
對於詢問三,有四個後綴」popoqqq」,」opoqqq」,」qqq」,」qq」,其中只有」qqq」,」qq」兩個後綴之間的LCP不爲0,且長度爲2,所以答案爲2.
對於100%的測試數據,有\(S<=5*10^5\),且\(\sum t<=3*10^6\).
特別注意:因爲另外一世界線的某些參數發生了變化,對於一組詢問,即便一個後綴出現了屢次,也僅算一次.


首先一個很顯然的思路就是直接把這個數組按照rank排序,而後咱們發現對於每一個點,前面的點的貢獻從前日後是單調不減的,而後就能夠直接維護單調棧了

挺水的題


#include<bits/stdc++.h>

using namespace std;

typedef pair<int, int> pi;
typedef long long ll;
const int N = 5e5 + 10;
const int M = 3e6 + 10;
const int LOG = 20;
const ll Mod = 23333333333333333;

struct Suffix_Array {
  int s[N], n, m;
  int c[N], x[N], y[N];
  int height[N], sa[N], rank[N];
  int st[N][LOG], Log[N];
  ll sum[N]; 
  
  void init(int len, char *c) {
    n = len, m = 0;
    for (int i = 1; i <= len; i++) {
      s[i] = c[i];
      m = max(m, s[i]);
    }
  }
  
  void radix_sort() {
    for (int i = 1; i <= m; i++) c[i] = 0;
    for (int i = 1; i <= n; i++) c[x[y[i]]]++;
    for (int i = 1; i <= m; i++) c[i] += c[i - 1];
    for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i];
  }
  
  void buildsa() {
    for (int i = 1; i <= n; i++) x[i] = s[i], y[i] = i;
    radix_sort();
    int now;
    for (int k = 1; k <= n; k <<= 1) {
      now = 0;
      for (int i = n - k + 1; i <= n; i++) y[++now] = i;
      for (int i = 1; i <= n; i++) if (sa[i] > k) y[++now] = sa[i] - k;
      radix_sort();
      y[sa[1]] = now = 1;
      for (int i = 2; i <= n; i++) y[sa[i]] = (x[sa[i]] == x[sa[i - 1]] && x[sa[i] + k] == x[sa[i - 1] + k]) ? now : ++now;
      swap(x, y);
      if (now == n) break;
      m = now;
    }
  }
  
  void buildrank() {
    for (int i = 1; i <= n; i++) rank[sa[i]] = i;
  }
  
  void buildsum() {
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + n - sa[i] + 1 - height[i];
  }

  void buildheight() {
    for (int i = 1; i <= n; i++) if (rank[i] != 1) {
      int k = max(height[rank[i - 1]] - 1, 0); // 裏面是 rank[i - 1] 
      for (; s[i + k] == s[sa[rank[i] - 1] + k]; k++);
      height[rank[i]] = k; // height 裏面是 rank 
    }
  }
  
  void buildst() {
    Log[1] = 0;
    for (int i = 2; i < N; i++) Log[i] = Log[i >> 1] + 1;
    for (int i = 1; i <= n; i++) st[i][0] = height[i];
    for (int j = 1; j < LOG; j++) {
      for (int i = 1; i + (1 << (j - 1)) <= n; i++) {
        st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
      }
    }
  }
  
  int queryst(int l, int r) {
    if (l == r) return n - sa[l] + 1;
    if (l > r) swap(l, r);
    ++l; //***
    int k = Log[r - l + 1];
    return min(st[l][k], st[r - (1 << k) + 1][k]);
  }
  
  int querylcp(int la, int lb) {
    return queryst(rank[la], rank[lb]);
  }
  
  void build(int len, char *c) {
    init(len, c);
    buildsa();
    buildrank();
    buildheight();
    buildsum();
    buildst();
  } 
} Sa;

int n, q, m;
char c[N];
ll ans, sum;
pi p[M];

struct Node {
  int num, pos;
  ll val;
};
stack<Node> Q;

ll add(ll a, ll b) {
  return (a += b) >= Mod ? a - Mod : a;
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &n, &q);
  scanf("%s", c + 1);
  Sa.build(n, c); 
  while (q--) {
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
      scanf("%d", &p[i].second);
      p[i].first = Sa.rank[p[i].second];
    }
    sort(p + 1, p + m + 1);
    m = unique(p + 1, p + m + 1) - p - 1;
    ans = sum = 0;
    while (Q.size()) Q.pop();
    Q.push((Node) {1, p[1].second, 0});
    for (int i = 2; i <= m; i++) {
      int curnum = 1, len = Sa.querylcp(Q.top().pos, p[i].second);
      while (Q.size() && Q.top().val >= len) {
        curnum += Q.top().num;
        sum -= Q.top().val * Q.top().num;
        Q.pop();
      }
      Q.push((Node) {curnum, p[i].second, len});
      sum += len * curnum; 
      ans = add(ans, sum);
    }
    printf("%lld\n", ans);
  }
  return 0;
}
相關文章
相關標籤/搜索