思路讲解
那么首先相邻,状态转移只需要考虑最后一个(前缀dp)。
AC代码
https://qoj.ac/submission/1125813
1 |
|
心路历程(WA,TLE,MLE……)
https://qoj.ac/submission/1125736
1 | // 最大值 次大值 |
那么首先相邻,状态转移只需要考虑最后一个(前缀dp)。
https://qoj.ac/submission/1125813
1 | #include <bits/stdc++.h> |
https://qoj.ac/submission/1125736
1 | // 最大值 次大值 |
这种方式更稳定,精度更高(其实就是用这种方式过了)
1 | // 方案二:使用固定迭代次数(100次)进行三分搜索。 |
https://codeforces.com/gym/105851/my#
1 | #include <bits/stdc++.h> |
给你一个字符串 s1,它是由某个字符串 s2 不断自我连接形成的(保证至少重复 2 次)。但是字符串 s2 是不确定的,现在只想知道它的最短长度是多少。
第一行一个整数 L,表示给出字符串的长度。
第二行给出字符串 s1 的一个子串,全由小写字母组成。
仅一行,表示 s2 的最短长度。
1 | 8 |
1 | 3 |
对于样例,我们可以利用 abc 不断自我连接得到 abcabcabcabc,读入的 cabcabca,是它的子串。
对于全部的测试点,保证 1≤L≤106。
给出子串,找出最短循环节。
最大公共前后缀。
结论就是N-pi[N]。其实我也不是很懂。
不能够使用二分,因为周期并没有单调性。
核心观察:字符串 s 的最小周期为 p,当且仅当 p 是满足以下条件的最小正整数:
si=si+p∀0≤i<L−p
这等价于:s 去掉前 p 个字符得到的后缀,与去掉后 p 个字符得到的前缀完全相同。即:
s[0…L−p−1]=s[p…L−1]
用多项式哈希预处理后,每次子串比较 O(1)。从 p=1 开始枚举,第一个满足条件的 p 就是答案。总复杂度 O(L)。
参考代码:
1 | #include <bits/stdc++.h> |
用了双哈希避免冲突。本质上和 KMP 中 L−πL 的经典做法等价,只是换了一种判断周期的手段。
这其实就是把逐字符的条件"打包"成了一个子串相等的条件,是同一件事的两种说法。
逐字符展开:两个长度相同的子串相等,就是对应位置的字符全部相等。把两个子串的对应位置写出来:
| 前缀中的第 i 个字符 | 后缀中的第 i 个字符 |
|---|---|
| s[0] | s[p] |
| s[1] | s[p+1] |
| ⋮ | ⋮ |
| s[i] | s[i+p] |
| ⋮ | ⋮ |
| s[L−p−1] | s[L−1] |
| 前缀的第 i 个字符是 s[i],后缀的第 i 个字符是 s[i+p]。 | |
| 所以"两个子串相等"⟺ 每一行都对应相等 ⟺ si=si+p 对所有 0≤i<L−p 成立。 | |
用例子直观感受:取 cabcabca,p=3: |
1 | 前缀(去掉后3个):c a b c a |
每个字符都和它后面第 p 个字符相同,这就是"周期为 p"的含义。把这些逐个比较合在一起看,就是前缀和后缀相等。
两种说法完全等价,只是视角不同:一个是逐字符看,一个是整体看。

把这个列出来以后,我们就知道了周期 p 的成立条件是什么。可以通过枚举 p,非常简单的使用字符串哈希检验答案。
https://www.luogu.com.cn/record/221541535
1 | // Problem: P4391 [BalticOI 2009] Radio Transmission 无线传输 |
https://cp-algorithms.com/string/prefix-function.html
【KMP 学一遍忘一遍?ACM 金牌选手用可视化直击本质,理解了内核后想忘记都难!】 【精准空降到 04:05】 https://www.bilibili.com/video/BV1Er421K7kF/?share_source=copy_web&vd_source=6ca0bc05e7d6f39b07c1afd464edae37&t=245
那么我们容易发现,这个

ABC……ABC+D,那么如果前缀字符串后面跟着D,那么就是pi函数=4,但是如果后面不是D,那要怎么办?
不难发现,S[i+1]=S[π[i]+1]⇒π[i+1]=π[i]+1(注意,这里我采用的是1-based,这更自然)。这不言而喻。(上面的这个图片已经解释了,我们再细化一下)

当然,这个是皆大欢喜的情况,但是如果出现失配了怎么办?

这个 pi_next(),为什么一定是这个 pi_next()。可以使用反证法。

okay,问题就来到了,怎么样使用前缀数组实现这个文本串和模式串的这个匹配?
1 | // 文本串 模式串 |
特别的,我们认为 i=1 的时候 pi[i]=0。


1 |
|
1 | // Problem: P3375 【模板】KMP |
AC
https://www.luogu.com.cn/record/269142391
1 | /** |
那么容易发现,其实这种区间操作的题目一般来说最短的操作是最灵活的,一个长操作也可以化为多个短操作的结果。
比如说这道题目长度为4的操作可以变为对中间两个元素执行一次,再对两侧两个元素分别执行一次。
所以我们不用考虑长度>2的操作。
那么不难注意到,就是说这个开头和结尾往往是这个破局的地方,那么开头那如果不同,必须要操作后面一个,然后不断传递,最后传递到A_[N],就结束了。
1 | FOR(i,1,N){ |
https://www.luogu.com.cn/record/221383853
1 | // Problem: P11643 【MX-X8-T2】「TAOI-3」终有一天,飞向水平线的彼方 |