Loj2083.「NOI2016」优秀的拆分
「NOI2016」优秀的拆分
#2083. 「NOI2016」优秀的拆分 - 题目 - LibreOJ (loj.ac)
求将字符串 所有子串拆分为 形式的总个数。
令 表示以位置 开头的 串的个数, 表示以位置 结尾的 串的个数。
那么最后答案为
字符串 可以 暴力求出 ,瓶颈在于如何快速求出 。
考虑求所有长度为 的 串,将原串每 位设置关键点,可以发现每个 串一定经过两个关键点。
发现可以以关键点为界将 分为 和 。
考虑求出两个相邻关键点 的 和 ,可以发现能形成 串的充要条件是 ,并且形成 串的开头为一段区间 ,结尾类似。
求出原串正反的 ,总时间复杂度 。
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
nclude <bits/stdc++.h>
using namespace std;
int sl[N + 1], sr[N + 1], Log[N + 1];
int n;
struct SA {
int sa[N + 1], rk[N + 1], ht[N + 1], id[N + 1], oldrk[N << 1], buc[N + 1], px[N + 1], ft[N + 1][L + 1];
int sk;
void mysort() {
fill(buc, buc + 1 + sk, 0);
fo(i, 1, n) ++ buc[ px[i] = rk[id[i]] ];
fo(i, 1, sk) buc[i] += buc[i - 1];
fd(i, n, 1) sa[ buc[px[i]] -- ] = id[i];
}
bool pd(int x, int y, int z) { return oldrk[x] == oldrk[y] && oldrk[x + z] == oldrk[y + z]; }
void build(char ch[]) {
Mes(rk, 0), Mes(oldrk, 0);
sk = 26;
fo(i, 1, n) rk[ id[i] = i ] = ch[i] - 'a' + 1;
mysort();
for (int w = 1, p = 0; w <= n; w <<= 1, p = 0) {
fo(i, n - w + 1, n) id[ ++ p ] = i;
fo(i, 1, n) if (sa[i] > w)
id[ ++ p ] = sa[i] - w;
mysort();
mcp(oldrk, rk);
sk = 0;
fo(i, 1, n)
rk[sa[i]] = pd(sa[i], sa[i - 1], w) ? sk : ++ sk;
if (sk == n) {
fo(i, 1, n) sa[rk[i]] = i;
break;
}
}
sk = 0;
fo(i, 1, n) {
if (sk) -- sk;
while (ch[i + sk] == ch[sa[rk[i] - 1] + sk])
++ sk;
ht[rk[i]] = sk;
}
fo(i, 1, n) ft[i][0] = ht[i];
fo(j, 0, L - 1) fo(i, 1, n)
ft[i][j + 1] = (i + (1 << j) <= n) ? min(ft[i][j], ft[i + (1 << j)][j]) : ft[i][j];
}
int dt;
int get(int l, int r) {
l = rk[l], r = rk[r];
if (l > r) swap(l, r);
if (++ l == r) return ft[l][0];
dt = Log[r - l + 1];
return min(ft[l][dt], ft[r - (1 << dt) + 1][dt]);
}
} s1, s2;
char ch[N + 1];
int main() {
int T; scanf("%d\n", &T);
Log[1] = 0;
fo(i, 2, N)
Log[i] = Log[i >> 1] + 1;
while (T --) {
scanf("%s\n", ch + 1);
n = strlen(ch + 1);
s1.build(ch);
fd(i, (n >> 1), 1)
swap(ch[i], ch[n - i + 1]);
s2.build(ch);
fill(sl + 1, sl + 1 + n, 0);
fill(sr + 1, sr + 1 + n, 0);
int l, r, lcp, lcs, dlen, pl, pr;
fd(len, (n >> 1), 1) {
fd(k, (n / len) - 1, 1) {
l = len * k, r = len * (k + 1);
lcs = s1.get(l, r);
lcp = s2.get(n - l + 1, n - r + 1);
if (lcp + lcs > len) {
lcp = min(lcp, len), lcs = min(lcs, len);
dlen = lcp + lcs - len - 1;
++ sl[l - lcp + 1], -- sl[l - lcp + 1 + dlen + 1];
++ sr[r + lcs - 1], -- sr[r + lcs - 1 - dlen - 1];
}
}
}
fo(i, 2, n) sl[i] += sl[i - 1];
fd(i, n - 1, 1) sr[i] += sr[i + 1];
long long ans = 0;
fo(i, 2, n) ans += 1ll * sr[i - 1] * sl[i];
printf("%lld\n", ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步