Manacher 模版
在 $O(n)$ 的时间复杂度内,求出字符串 str
内部的所有回文字符串。因为回文串数量级别可能到达 $O(n^2)$,所以这里求出的是具有代表性的回文串信息,即以 str
内任意位置为中心的最长回文串。
使用方法
- 调用
build
函数,预处理字符串s
的所有回文信息,函数返回s
内最长的回文字符串长度,复杂度 $O(n)$。你需要先 bulid 才可以执行下列查询操作。(Python 代码这一步为类的构造函数,并不返回最长回文长度) mid(x, true/false)
函数可以返回以某一个位置为中心的最长回文串,mid(x, true)
返回以字符串下标为 $x$ 的字母为中心的最长奇回文串长度,mid(x, false)
,返回以字符串下标 $x,x+1$ 两个字母中间为中心的最长偶回文串长度,复杂度为 $O(1)$。left(x)
函数可以返回从 $x$ 位置开始的最长回文串的长度,复杂度 $O(1)$。
字符串的下标均从 0 开始。
Left 数组
为了方便模板使用,额外维护了一个 Left
数组,为了计算以某一个字符为起点的最长回文子串的长度。
Left
数组的定义:在扩展之后的字符串上,以位置 $i$ 为起点的最长回文子串的中心在哪里。
Left
的计算有一点动态规划的意思:
C++ 代码的 21 行,我们先用得到的 ans
数组(每一个中心对应的最长回文子串中心到边界的距离)的值初始化 Left
数组,这样每一个最长回文子串的信息就对应左端点的 Left
数组中了。
C++ 代码 22 行,我们把回文信息扩展到所有的 Left
数组中。假设 $i<j$,且有 $Left[i]>Left[j]$,那么我们就可以将 $Left[j]$ 更新为 $Left[i]$,实际上相当于用了中心在 $Left[i]$ 的最长回文自子串的一个子串更新了 $Left[j]$。
查询的时候,我们知道了每一个起点的对应的最远回文中心,就可以将其相应的答案恢复出来。
C++ 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
namespace Manacher{ vector<int> ans, str, lef; int build(const string &s){ // 初始化 int n = s.length(), m = (n + 1) << 1, ret = 0; str.resize(m + 1); ans.resize(m + 1); lef.resize(m + 1); // $ - 串开始标记, @ - 串结束标记,# - 将字母间隔开。注意这里的字符必须是字符串内不存在的 str[0] = '$'; str[m] = '@'; str[1] = '#'; ans[1] = 1; for (int i = 1; i <= n; i++) str[i << 1] = s[i - 1], str[i << 1 | 1] = '#'; // Manacher 操作。r, p 分别维护当前最靠右最长回文串的右边界与中心 ans[1] = 1; for (int r = 0, p = 0, i = 2; i < m; ++i){ if (r > i) ans[i] = min(r - i, ans[p * 2 - i]); else ans[i] = 1; // Manacher 核心操作 while(str[i - ans[i]] == str[i + ans[i]]) ++ans[i]; // 暴力向外扩展 if (i + ans[i] > r) r = i + ans[i], p = i; // 尝试更新 r, p ret = max(ret, ans[i] - 1); // 更新答案 } // 计算维护以每个位置为起点的最长回文串 for (int i = 0; i <= m; i++) lef[i] = 0; for (int i = 2; i < m; i++) if (lef[i - ans[i] + 1] < i + 1) lef[i - ans[i] + 1] = i + 1; for (int i = 1; i <= m; i++) if (lef[i] < lef[i - 1]) lef[i] = lef[i - 1]; return ret; } int mid(int x, bool odd){ if (odd) return ans[(x + 1) << 1] - 1; return ans[(x + 1) << 1 | 1] - 1; } int left(int x){ return lef[(x + 1) << 1] - ((x+1) << 1); } } |
Python 代码
代码与 C++ 基本对应,故注释比较简单。
调用构造函数传入字符串 S
进行 Manacher 预处理,等同于 C++ 的 build
函数。注意,这步不返回 S
内最长回文串的长度,取而代之需要调用 max()
函数获得这个值,复杂度 $O(1)$。
left
与 mid
函数意义不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class Manacher: def __init__(self, S: str): m, ret = len(S) * 2 + 2, 0 ans = [0] * (m + 1) # 构造扩展字符串 T = ['$', '#'] for c in S: T.append(c) T.append('#') T.append('@') # Manacher ans[1], r, p = 1, 0, 0 for i in range(2, m): ans[i] = min(r - i, ans[p * 2 - i]) if r > i else 1 while T[i - ans[i]] == T[i + ans[i]]: ans[i] += 1 if i + ans[i] > r: r, p = i + ans[i], i ret = max(ret, ans[i] - 1) # 计算左起最长回文长度 lef = [0] * (m + 1) for i in range(2, m): lef[i - ans[i] + 1] = max(lef[i - ans[i] + 1], i + 1) for i in range(1, m + 1): lef[i] = max(lef[i], lef[i - 1]) self.ans, self.lef, self.ret = ans, lef, ret def mid(self, x, odd): if odd: return self.ans[x * 2 + 2] - 1 return self.ans[x * 2 + 3] - 1 def left(self, x): return self.lef[x * 2 + 2] - x * 2 - 2 def max(self): return self.ret |
C++ 压缩代码
方便打印或网赛复制使用,压缩代码不是好习惯,大家看看就行……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
namespace Manacher{ vector<int> ans, str, lef; int build(const string &s){ int n = s.length(), m = (n + 1) << 1, ret = 0; str.resize(m + 1); ans.resize(m + 1); lef.resize(m + 1); str[0] = '$'; str[m] = '@'; str[1] = '#'; ans[1] = 1; for (int i = 1; i <= n; i++) str[i << 1] = s[i - 1], str[i << 1 | 1] = '#'; for (int r = 0, p = 0, i = 2; i < m; ret = max(ret, ans[i++] - 1)){ for (ans[i] = r > i ? min(r - i, ans[p * 2 - i]) : 1; str[i - ans[i]] == str[i + ans[i]]; ++ans[i]); if (i + ans[i] > r) r = i + ans[i], p = i; } for (int i = 0; i <= m; i++) lef[i] = 0; for (int i = 2; i < m; i++) if (lef[i - ans[i] + 1] < i + 1) lef[i - ans[i] + 1] = i + 1; for (int i = 1; i <= m; i++) if (lef[i] < lef[i - 1]) lef[i] = lef[i - 1]; return ret; } int mid(int x, bool odd){ if (odd) return ans[(x + 1) << 1] - 1; return ans[(x + 1) << 1 | 1] - 1; } int left(int x){ return lef[(x + 1) << 1] - ((x+1) << 1); } } |

原文链接:Manacher 模版
WNJXYKの博客 版权所有,转载请注明出处。
还没有任何评论,你来说两句吧!