0%

Codeforces Round 1087 (Div. 2)——CF-2209-E. A Trivial String Problem(如何计算最短 border?)

题目大意

题目描述

定义函数 f(t)f(t) 为:将字符串 tt 划分为若干部分的最大数量,满足每个部分都是 tt非空前缀

There exist kk strings p1,p2,,pkp_1,p_2,\ldots,p_k, which are prefixes of tt, such that t=p1+p2++pkt=p_1+p_2+\ldots+p_k. Here ++ denotes the string concatenation.

换句话说,f(t)f(t) 是满足以下条件的最大正整数 kk存在 kk 个字符串 p1,p2,,pkp_1, p_2, \dots, p_k,它们都是 tt 的前缀,且 t=p1+p2++pkt = p_1 + p_2 + \dots + p_k++ 表示字符串拼接)。

给定一个长度为 nn 的小写英文字符串 ss
你需要回答 qq 次询问,每次询问给出两个整数 lil_irir_i,要求计算并输出:
j=lirif(s[lij])\sum_{j=l_i}^{r_i} f(s[l_i \dots j])
(其中 s[lij]s[l_i \dots j] 表示 ss 从第 lil_i 个字符到第 jj 个字符组成的子串)。

image

这个其实是方便我们进行计算的。

输入格式

第一行包含一个整数 tt1t1031 \le t \le 10^3),表示测试用例的数量。
每个测试用例的第一行包含两个整数 nnqq1n1061 \le n \le 10^6, 1q1001 \le q \le 100),分别表示字符串长度和询问次数。
第二行包含一个长度为 nn 的小写英文字符串 ss
接下来 qq 行,每行包含两个整数 lil_irir_i1lirin1 \le l_i \le r_i \le n),表示一次询问。

保证所有测试用例中 nn 的总和不超过 10610^6

输出格式

对于每次询问,输出一个整数表示表达式的求和结果。

样例数据

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
输入:
6
1 1
a
1 1
5 2
aaaaa
1 5
2 4
6 2
abcdef
1 6
3 5
6 3
abaaba
1 6
1 3
2 6
7 3
abcabca
1 7
2 7
4 7
8 3
aababaac
1 8
2 8
3 7

输出:
1
15
6
6
3
14
4
7
12
9
5
13
14
7

样例解释

在第一个测试用例中,f(a)=1f(\text{a}) = 1

在第二个测试用例中:
对于第一次询问 1 5f(a)+f(aa)+f(aaa)+f(aaaa)+f(aaaaa)=1+2+3+4+5=15f(\text{a}) + f(\text{aa}) + f(\text{aaa}) + f(\text{aaaa}) + f(\text{aaaaa}) = 1 + 2 + 3 + 4 + 5 = 15
对于第二次询问 2 4(对应的子串是 aaa):f(a)+f(aa)+f(aaa)=1+2+3=6f(\text{a}) + f(\text{aa}) + f(\text{aaa}) = 1 + 2 + 3 = 6

在第三个测试用例中:
对于第一次询问 1 6f(a)+f(ab)+f(abc)+f(abcd)+f(abcde)+f(abcdef)=1+1+1+1+1+1=6f(\text{a}) + f(\text{ab}) + f(\text{abc}) + f(\text{abcd}) + f(\text{abcde}) + f(\text{abcdef}) = 1 + 1 + 1 + 1 + 1 + 1 = 6
对于第二次询问 3 5(对应的子串是 cde):f(c)+f(cd)+f(cde)=1+1+1=3f(\text{c}) + f(\text{cd}) + f(\text{cde}) = 1 + 1 + 1 = 3

思路讲解

P3375 【模板】KMP(前缀函数)(完全理解 kmp)

队友的这个思路,确实是非常好啊。

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vector<ll> shortest_prefix(const vector<ll> &pi) {
vector<ll> sp(SZ(pi));
for (int i = 1; i < SZ(pi); ++i) {
if (pi[i] == 0) {
sp[i] = 0;
} else if (sp[pi[i] - 1] > 0) {
sp[i] = sp[pi[i] - 1];
} else {
sp[i] = pi[i];
}
}
return sp;
}

这个方法的正确性还是比较容易看出来的,不言自明,只要前缀和后缀相同。其之所以有效,和 kmp 一样。

image

AC代码

AC
https://codeforces.com/contest/2209/submission/367859558

心路历程(WA,TLE,MLE……)