【BZOJ4650】【UOJ219】【NOI2016】优秀的拆分

xiaoxiao2021-03-01  7

【题目链接】

BZOJUOJ

【思路要点】

定义前后一半相同的字符串为优秀的字符串,记\(pre_i\)表示以\(i\)结尾的优秀的字符串的个数,\(suf_i\)表示以\(i\)开头的优秀的字符串的个数。则答案为\(\sum_{i=1}^{N-1}pre_i*suf_{i+1}\)。显然\(pre\)和\(suf\)的处理方式类似,考虑如何计算\(pre\)。考虑枚举所求的优秀的字符串长度的一半\(i\),并枚举两点\(x\)和\(y\)满足\(x+i=y\),我们希望统计出所有覆盖\(x\)和\(y\)的长度为\(2*i\)的优秀的字符串对\(pre\)数组的贡献,这样我们下一次枚举就不必枚举\((x+1,y+1)\),而是\((x+i,y+i)\)了。令从\(x\)和\(y\)开始,向前能匹配的字符数为\(p\),向后能匹配的字符数为\(s\)。令\(p=Min\{p,i\},s=Min\{s,i\}\)。那么这一对\((x,y)\)会对所有满足\(i\in[x-p+i*2,y+s-1]\)的\(pre_i\)产生1的贡献。差分+前缀和即可。时间复杂度\(O(TNLogN)\)。

【代码】

#include<bits/stdc++.h> using namespace std; const int MAXN = 60005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } namespace SuffixArray { const int MAXN = 60005; const int MAXLOG = 20; const int MAXC = 256; int sa[MAXN], rank[MAXN], height[MAXN]; int Min[MAXN][MAXLOG], bit[MAXN], N; void init(char *a, int n) { memset(sa, 0, sizeof(sa)); memset(rank, 0, sizeof(rank)); memset(height, 0, sizeof(height)); memset(Min, 0, sizeof(Min)); N = n; static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN]; memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[(int) a[i]]++; for (int i = 1; i <= MAXC; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) sa[cnt[(int) a[i]]--] = i; rank[sa[1]] = 1; for (int i = 2; i <= n; i++) rank[sa[i]] = rank[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]); for (int k = 1; rank[sa[n]] != n; k <<= 1) { for (int i = 1; i <= n; i++) { x[i] = rank[i]; y[i] = (i + k <= n) ? rank[i + k] : 0; } memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[y[i]]++; for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) rk[cnt[y[i]]--] = i; memset(cnt, 0, sizeof(cnt)); for (int i = 1; i <= n; i++) cnt[x[i]]++; for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i]; rank[sa[1]] = 1; for (int i = 2; i <= n; i++) rank[sa[i]] = rank[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]); } int now = 0; for (int i = 1; i <= n; i++) { if (now) now--; while (a[i + now] == a[sa[rank[i] + 1] + now]) now++; height[rank[i]] = now; } for (int i = 1; i <= n; i++) Min[i][0] = height[i]; for (int p = 1; p < MAXLOG; p++) { int tmp = 1 << (p - 1); for (int i = 1, j = tmp + 1; j <= n; i++, j++) Min[i][p] = min(Min[i][p - 1], Min[i + tmp][p - 1]); } for (int i = 1; i <= n; i++) { bit[i] = bit[i - 1]; if (i >= 1 << (bit[i] + 1)) bit[i]++; } } int lcp(int x, int y) { if (x == y) return N - x + 1; x = rank[x], y = rank[y]; if (x > y) swap(x, y); int tmp = bit[y - x]; return min(Min[x][tmp], Min[y - (1 << tmp)][tmp]); } } char s[MAXN]; int pre[MAXN], suf[MAXN]; int main() { int T; read(T); while (T--) { scanf("%s", s + 1); int n = strlen(s + 1); int tmp = n; s[++tmp] = '#'; for (int i = n; i >= 1; i--) s[++tmp] = s[i]; s[tmp + 1] = 0; SuffixArray::init(s, tmp); tmp = 2 * n + 2; memset(pre, 0, sizeof(pre)); memset(suf, 0, sizeof(suf)); for (int len = 1; len <= n; len++) { for (int i = 1, j = len + 1; j <= n; i += len, j += len) { int s = SuffixArray::lcp(i, j); int p = SuffixArray::lcp(tmp - i, tmp - j); chkmin(s, len), chkmin(p, len); int l = i - p + len * 2, r = j + s - 1; if (l <= r) { pre[l]++; pre[r + 1]--; } l = i - p + 1, r = j + s - len * 2; if (l <= r) { suf[l]++; suf[r + 1]--; } } } for (int i = 1; i <= n; i++) { pre[i] += pre[i - 1]; suf[i] += suf[i - 1]; } long long ans = 0; for (int i = 1; i <= n - 1; i++) ans += 1ll * pre[i] * suf[i + 1]; writeln(ans); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-3650165.html

最新回复(0)