问题链接: https://www.luogu.com.cn/problem/P3805
PART 1.回文串的中心问题:
形如aba , abba【下文称作有两个中心的回文(子)串】都算是回文串。
若我们在寻找回文子串的过程中暴力枚举假设s[i](0<=i<s.size())为s中的一个回文子串的中心,那么形如ABBA这样的回文子串就会被我们忽略掉
为了解决这样的问题,伟大的Manacher采用了一种伟(离)大(谱)&有效的方法,下面我来演示一遍
原字符串:abba
经Manacher之手的字符串:#a#b#b#a#
找!处理后的字符串找到一个有两个中心的回文串我都请吃饭!!!
显而易见,咱们只需要在原字符串的每一个字符前后加上一个相同的字符可以解决像abba这样的字符串在枚举中心时带来的烦恼了
A little little detail:我们插入的辅助字符是不可以与原字符串中可能出现的任何字符相同的,因为如果有两个相同的字符挨在一起的话,那就会形成一个新的有两个中心的回文子串,就不能达到我们插入相同字符使有两个中心的回文子串消失的目的了
好耶!我们可以继续毫无烦恼地暴力找最长回文子串了!!!
NO!像Manacher这样的神仙,怎么可能允许自己撒敷敷地暴力寻找回文子串勒:)
PART 2.每个回文串的半径问题:
解决了枚举中心的问题,现在,我们只需要找到以每个字符为回文中心所得的回文串半径就可以找到最大的回文串长度以解决我们的问题了(忘记问题的->https://www.luogu.com.cn/problem/P3805 )
在学习核心内容之前,我有必要阐明一些东西:
1.对于一个经处理的回文串(#A#B#B#A#),有且仅有一个对称中心。且叫它回文对称中心。
2.在一个回文串内,任选一段区间 X ,一定存在关于"回文对称中心"对称的一个区间 Y ,且把这个区间 Y 叫做关于区间 X 的对称区间。
3.区间和对称区间一定全等。(你都是对称的怎么可能不全等)
4.若一个区间的对称区间是回文串,这个区间必定是一个回文串。在大的回文串内(指包含当前区间的一部分或全部的回文串),它们回文半径相等。
5.然而我们通过确定关系预先得到的回文半径,它的数值,必定小于等于这个位置真实的回文串半径。
前文已经提到过马拉车的时间复杂度是O(n),如果我们要求一个字符串里面的最长回文子串,那么枚举每个字符作为回文中心求回文子串长度便是必不可少的,那么,我们该怎么样才能把O(n^2)的时间复杂度降到O(n)呢?
若我们可以借助之前求出来的条件省去一部分匹配相同字符&找回文串半径的时间,那么省下来的时间将会使时间复杂度大大降低
在一个目前求出的所有回文子串中右端点最靠右的回文子串中,我们记录其右端点(下文称r_est)及其中心(下文称r_mid),接下来在枚举字符(s[i])作为回文中心时,如果它在我们所记录的这个右端点最靠右(也就是中心为r_mid,)的回文字串内部,那么必定有:(设该以s[i]为中心的回文字串半径为p[i])为其在未循环匹配的情况下至少会有的回文串半径:
上推导:
现我们已知以s[j]为中心的回文字串与以s[i]为中心的回文字串 关于回文中心r_est对称且全等 ,而我们之前已经求出来了以s[j]为中心所形成的回文字串的回文半径p[j],根据中点公式乘二得到
,移项得
,然而我们并不能确定以r_mid为中心所形成的回文字串以外的字符是否在以s[i]为中心的回文字串中形成回文,且我们也不能保证以s[j]为中心的回文字串是否全部分都在以r_mid为中心的回文字串内部,故我们仅能将以s[j]为中心的回文串的处在较大串内的部分对称到s[i]区间
故有
好了,到这里,Manacher的核心部分就已经讲完了
Part 3.防止越界的小小技巧(可跳过)
该部分只是一个简单小技巧,只要你保证敲代码时不判断错误,这部分就可以跳过了
当我们while暴力判断字串是否可在具有回文串性质的前提下尽量大时,(说人化话就是暴力找更大回文串时 )我们如果边界判断不当,就容易字符数组越界(好耶RE了!)
于是乎,就有某位小天才想到:
只要咱们让它在到达边界时发现非回文,不就可以退出while自动防止越界了嘛?
实际操作也超简单,嘿嘿
只要咱们在字符串首加入一个整个字符串都没有的字符,然后咱们就能完成咱们的防越界大业啦!