(我并不想告诉你题目名字是什么鬼)c++
有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].数组
如今有若干组询问,对于每个询问,咱们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍测试
第一行两个正整数n,m,分别表示S的长度以及询问的次数.ui
接下来一行有一个字符串S.spa
接下来有m组询问,对于每一组询问,均按照如下格式在一行内给出:code
首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置排序
对于每一组询问,输出一行一个整数,表示该组询问的答案.因为答案可能很大,仅须要输出这个答案对于23333333333333333(一个巨大的质数)取模的余数.ip
7 3字符串
popoqqqinput
1 4
2 3 5
4 1 2 5 6
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; }