【题解】Solution Set - NOIP2024集训Day58 字符串
【题解】Solution Set - NOIP2024集训Day58 字符串
https://www.becoder.com.cn/contest/5658
「CF1466G」Song of the Sirens
考虑对于 \(s_i\),算钦定必须覆盖到 \(t_i\) 的匹配个数 \(f_i\),其对答案的贡献为 \(2^{k-i}f_i\)。
注意到 \(s\) 每次长度都会 \(\times ~2\) 左右,其长度在 \(O(\log |w|)\) 的时候就会超过 \(|w|\),而在此之后 \(f_i\) 可能匹配到的字符串只会有 \(t_i\) 这一位不同。
对于后面这一部分,我们直接 \(O(w)\) 暴力枚举 \(w\) 的每一位作为 \(t_i\),来算 \(f_i\)。
而前面这一部分,每一次 \(O(w)\) 维护 hash,同样 \(O(w)\) 算 \(f_i\)。一共 \(O(\log |w|)\) 次。
所以总时间复杂度为 \(O(w\log w)\)。
22min
「QOJ9372」Prefix of Suffixes
显然把 \(B_i\) 提出来,即有:
现在的问题在于如何去维护后面这个 \(A\) 的区间和。
考虑从上一次的答案继承过来。(下面的 \(n\) 都是在加入 \(s_i\) 后的 \(n\)。
显然,\(i=n\) 可以单独处理,而对于一个 \(z_i,i<n\) 其要增大的一个必要条件是 \(z_i=n-i\) 也即 \([i,n)\) 成为一个 border。也就是说对于 \(n-1\) 的每个 border 设其长度为 \(l\),如果 \(s_l=s_i\) 那么这个 \(z_i\) 可以累加 \(1\) 并成为新的 border。我们要算这些 border 的 \(B\) 的总和,然后和 \(A_i\) 相乘累加到答案。
每次可以在 \(O(\log n)\) 的时间复杂度内,遍历每个本质不同的 border。具体的做法是:
对于一个字符串 \(s\)。
设当前的 border 长度为 \(l\)。分讨:
\(2l\le |s|\):直接暴力跳就行了;
\(2l>|s|\):
设 \(k=|s|-l\),则 \(s[1\dots k]\) 为 \(s\) 的一个周期。
由 Weak Periodicity Lemma 的推论可以知道:
此时 \(s\) 的 border 集合中长度 \(\ge k+(|s|\bmod k)\) 的元素一定构成集合 \(\{pk+(|s|\bmod k)|p\in\mathbb N^{+}\}\)。
详细证明可以见 这篇题解。
对于这些这些 border 她们一般会有一些良好的性质。比如这道题里面每个 border 的后一位均相同。
既然如此,我们把这些的 \(B\) 打包起来一起算。
然后下一次直接跳到 \(k\),而 \(2k< |s|\)。
综上,我们每次跳都能使位置减半,故能做 \(O(\log n)\) 的时间复杂度。
26min
膜拜高手的神秘线性做法:https://www.becoder.com.cn/submission/2664090
前面部分大体差不多,只是后面算 \(B\) 的总和(也即高手所说的 \(g\))的时候,高手也是从上一次继承过来的,而我是直接较暴力的 \(O(\log n)\) 来算的。
自己实现了一下,发现那些 \(B\) 并不太能打包起来算,还是得从上一位继承过来。
另外一份也是 \(O(n\log n)\) 但是从之前继承的题解:https://www.cnblogs.com/cjjsb/p/18427542#c-prefix-of-suffixes
题外话,来源似乎是:https://www.cnblogs.com/yyyyxh/p/KMP-Z.html
「CEOI2011」Matching
直接枚举每一个长度为 \(n\) 的子串。我们维护一个 \(\sum P^{c}i\),\(c\) 表示比 \(i\) 小的个数。然后去跟 \(\sum P^{i-1}a_i\) 比。
总感觉和 这道题 有点莫名相像。(实际上代码也是这道题改的。
7min
(上面写的有点模糊,具体可以见代码
「POI2011」Periodicity
首先答案的第一位一定是 \(0\)(否则反转所有位一定更优。
感觉上来说有点像反悔贪心。
18min(没什么思路。
https://www.luogu.com.cn/article/2jepwn0f
注意 Weak Periodicity Lemma 及其推论。具体可见这篇题解。
(基于此,我们还可以对于任意前缀在 \(O(\log n)\) 的时间复杂度内,找出其所有的 border(注意是找出不代表个数。
第四个情况的分讨是真的把我整破防了。
「POI2005」Sza-Template
首先一个必要条件是答案必须是原串 border。(对但是好像没什么用。
暴力一点,直接建出 sam,然后线段树合并求出每个等价类的的 endpos。
枚举每个等价类,比较 endpos 之间差的最大值和等价类中最长子串长度 \(len\)。(注意特判开始和结尾必须等于 \(len\) 和 \(n-len+1\)。
11min(个人感觉没什么问题,实现虽然板但是比较多。
https://www.luogu.com.cn/record/184023802
对的但是被卡空间了。
https://www.luogu.com.cn/article/hd3hmymb
好像很有道理,可以直接 dp。
「POI2006」OKR-Periods of Words
做过。(但是当时好像没弄懂。
其实就是求最长循环节,也即长度 - 最短 border
而一般的 kmp 求的是最长 border。
我们再跑一遍,每次在 \(nxt\ne0\) 的情况下尽可能的跳 \(nxt\),这样就是最短的 border 了。