求回文子串
LeetCode 647
思路参考这里
代码实现
class Solution {
public:
// Manacher 模板
int countSubstrings(string s) {
// [STEP1] 修改源字符串
// 将 s 改造为 t,这样就不需要讨论 s.length() 的奇偶性,因为新串 t 的每个回文子串都是奇回文串(都有回文中心)
// s 和 t 的下标转换关系:
// (si+1)*2 = ti
// ti/2-1 = si
// ti 为偶数,对应奇回文串(从 2 开始)
// ti 为奇数,对应偶回文串(从 3 开始)
string t = "^";
for (char c : s) {
t += '#';
t += c;
}
t += "#$";
// [STEP2] 计算回文半径
// 定义一个奇回文串的回文半径=(长度+1)/2,即保留回文中心,去掉一侧后的剩余字符串的长度
// half_len[i] 表示在 t 上的以 t[i] 为回文中心的最长回文子串的回文半径
// 即 [i-half_len[i]+1,i+half_len[i]-1] 是 t 上的一个回文子串
vector<int> half_len(t.length() - 2);
half_len[1] = 1;
int ans = 0;
// box_r 表示当前右边界下标最大的回文子串的右边界下标+1
// box_m 为该回文子串的中心位置,二者的关系为 r=mid+half_len[mid]
int box_m = 0, box_r = 0;
for (int i = 2; i < half_len.size(); i++) {
int hl = 1;
if (i < box_r) {
// 记 i 关于 box_m 的对称位置 i'=box_m*2-i
// 若以 i' 为中心的最长回文子串范围超出了以 box_m 为中心的回文串的范围(即 i+half_len[i'] >= box_r)
// 则 half_len[i] 应先初始化为已知的回文半径 box_r-i,然后再继续暴力匹配
// 否则 half_len[i] 与 half_len[i'] 相等
hl = min(half_len[box_m * 2 - i], box_r - i);
}
// 暴力扩展
// 算法的复杂度取决于这部分执行的次数
// 由于扩展之后 box_r 必然会更新(右移),且扩展的的次数就是 box_r 右移的次数
// 因此算法的复杂度 = O(t.length()) = O(n)
while (t[i - hl] == t[i + hl]) {
hl++;
box_m = i;
box_r = i + hl;
}
half_len[i] = hl;
ans += hl / 2;
}
return ans;
}
};