1链接和代码
https://cp-algorithms.com/string/manacher.html
vector<int> manacher_odd(string s) {
int n = s.size();
s = "$" + s + "^";
vector<int> p(n + 2);
int l = 0, r = -1;
for(int i = 1; i <= n; i++) {
p[i] = max(0, min(r - i, p[l + (r - i)]));
while(s[i - p[i]] == s[i + p[i]]) {
p[i]++;
}
if(i + p[i] > r) {
l = i - p[i], r = i + p[i];
}
}
return vector<int>(begin(p) + 1, end(p) - 1);
}
2 相关说明
- 这里虽然函数名称含有odd这一词语,但是并不是说只适用于寻找字符串中所有长度为奇数的回文字符串,不然这个函数的应用就太受限了。个人理解是:该算法的思路是基于长度为奇数的回文字符串产生的,但是实际上长度为偶数的回文字符串也同样适用,不妨这么理解,偶数回文字符串中间两个字符间存在一个隐藏的中间字符,但是这个字符是否显式存在不重要。(如果读者有其他理解,欢迎讨论)
- 代码中巧妙地在字符串的开头和结尾补上了两个无关字符,即非数字非英文字母,这样做可以避免对左右边界的判断。可能读者会问了,只要回文字符串的左右边界不断向两边延伸,即使不会碰到原始字符串的边界,但还是会碰到"$“和”^",难道不会进一步越界?对,不会,因为到这两个边界的时候,必然会因为不匹配就退出了
- 初始化时这里还可以设置为l=0,r=1,这对结果没有影响
- 特别注重对max(0, min(r - i, p[l + (r - i)]))的理解,给定链接已经说明需要分两种情况,第一种就是r>=i的情形,这种情况下最直观的结果是r-i>=0, l+(r-i)>=l ,但是还有一层隐藏的关系在里面,这时候的i一定是【l, r】区域的中间值(l+r)/2的右侧位置, l+(r-i)一定在中间值(l+r)/2的左侧位置。这是因为如果i刚好等于这个回文字符串中间值的时候,l和r的值必然是在此时发生更新的,所以当i小于中间值的时候,这个时候的l和r还不是现在我们考虑的这种情形中的值。 r-i是限制当前的回文字符串半径长度不能超出当前的范围(存在大于r的值也属于以i为中心的回文字符串);而p【l+(r-i)】则是当前【l, r】区域中与i位置相对称的位置为回文字符串中心时的回文串最大长度,这个在前面的遍历过程中已经被计算出来了,这限制了以位置i为回文串中心的回文串最大长度。