manacher学习笔记
小学一下。
首先是用一个在回文串题目中的的技巧,用来减少分讨,如果想到这个的话说不定thusc2024 d1t1就切了。具体来说,就是在每个字符之间都插入一个\(\#\),然后在开头和结尾插入随便两个不同的字符。然后就只有回文中心在字符上的情况了。
首先设\(p_{i}\)为当前位置为中心的最大回文半径。\(mr\)为当前做到的回文串的右端点,\(mid\)为当前做到的回文串的中心。
首先i肯定大于\(mid\),因为根据代码,\(mid\)由\(i\)更新,而i是一直在前进的
分两类讨论:
\(mid < i < mr\)
\(p[i]=min(p[mid*2-i],mr-i+1)\)
分两部分解释。设j为i关于\(mid\)的对称点,\(ml\)为\(mr\)关于\(mid\)的对称点。如果\(i+p[j]>mr\),那么我们不能保证\([i-p[j],i+p[j]]\)是回文串,所以为\(mr-i+1\)。否则根据对称性,\(p[i]\)就等于\(p[j]\)

\(i>=mr\)
直接暴力向两边拓展即可。
时间复杂度证明
如果当前\(p[i]==p[j]\)那么说明不能向两边拓展了,就不会进到while里去。如果当前\(p[i]=mr-i+1\),那么就可能向两边拓展,同时更新\(mr\)。向两边拓展也更新了\(mr\),所以每次的while循环都对应着\(mr\)的拓展,每次\(mr\)的拓展都对应着while循环,因为\(mr\)最多为\(len\),那么最多进\(len\)次while,所以时间复杂度线性
注意manacher求的是\(p[i]\)就像kmp求的是\(nxt\)一样,用的是这个工具来解决题目,而不是只是记住模板上的东西。
然后做了%%%lyc%%%给的题单,感觉会了
P4555
一个比较自然的想法就是将两个字符串拼在一起。具体来说就是枚举一个中心,然后长度加上以\(i+p[i]+1\)为开头的最长字符串长度。枚举好求,考虑后面怎么求。我们可以从后往前,\(l[i]=max(l[i+1]-2,p[i])\),然后就以了。
P1659
manacher还会和一些乘法原理搞在一起然后就有一些奇妙的计数题。比如这题。
我们要首先知道\(p[i]\)代表的是最长回文半径。所以就有回文半径为\(1-p[i]\)的回文串各一个。这个可以用差分给他优化掉。我们统计出来后,我们就可以从高向低枚举,再用个快速幂就可以了。
P4287
计数题。有两种思路,一种是从kmp的角度考虑的,一种是从manacher的角度考虑的。
1.考虑在kmp的过程中,如果有一个匹配到了,那么就要加上有多少个\(i-p[i]<=j&&i+p[i]>=j+m\)的,这可以看成给一段区间进行区间加,然后再进行区间取\(min\)。第一个用差分实现,第二个用单调队列实现。时间\(O(n)\)
2.考虑在,manacher的过程中,找到了一个最长回文串,那么就加上它的所有贡献。我们可以将s2出现的位置的b数组加上1,那么问题就变成了\(\sum^{i+p[i]-m}_{j=i-p[i]} b[i]\)。我们可以这样搞

显然\(s1,s3\)就是\(i*b[i]\)的前缀和,\(s2\)就是\(b[i]\)的前缀和,这都可以\(O(n)\)求出
CF17E
首先两个回文串相交的条件就是一个回文串的右端点大于等于一个回文串的左端点。所以我们可以统计有多少个右端点大于\(i-p[i]\)。因为\(p[i]\)代表的是最长回文半径所以我们就有一个求和式子

我们可以搞两个线段树,一个存\(i*b[i]\)前缀和一个存\(b[i]\)前缀和,然后统计完答案后\([i,i+p[i]]\)整体加就可以了。对于\(i+b[i]\)也无非就是线段树里稍微搞一下就行了,这是\(O(n \log n)\)的做法
当然我们也可以统计一个串和所有串的答案,最后再减去重复的,但因为这题要取模,所以不好搞(开个long long貌似可以?),这样是\(O(n)\)的
最后我们可以正难则反。先求出所有的,再减去不相交的,我们可以统计出以i为开头和以i为结尾的回文串个数,\(sum1\),\(sum2\)然后搞一个\(sum2\)的前缀和,然后减掉\(sum1[i]*sum2[i-1]\)这个做法非常的好,我的做法简直是依托。这种问题经常有,不知道为什么。
P3501
manacher是字符串算法,但它不是回文串算法,我觉得以这个作为manacher学习笔记的结尾非常适合。manacher能做的是字符之间有匹配关系,且这个匹配关系大概长这个样子

这个样子的manacher应该都能做,这也算是对manacher的一种拓展了吧

浙公网安备 33010602011771号