Manacher 算法

又称 马拉车算法,用于求每个回文中心对应的最长回文子串的长度

模板

namespace string_algo {

  
    template <typename value_t, typename output_iter_t, ass_is_RAI(output_iter_t)>
        void manacher(const basic_string<value_t> &s, output_iter_t even, output_iter_t odd){
            // even_i 为 s_{i-even_i+1} ~ s_{i+even_i} 为回文串的最大值 (0<=i<n-1), odd_i 为 s_{i-odd_i} ~ s_{i+odd_i} 为回文串的最大值 (0<=i<n)
            if (s.empty())return;
            size_t n = s.size();
            if (n == 1){*odd = 0;return;}
            if (s[0] == s[1])*even = 1;else *even = 0;
            for (size_t i = 1, l = 1 - *even, r = *even; i < n; ++i){// [l,r] 为满足 ((l+r-1)/2) < i 且 r 最大的回文子串
                even[i] = i >= r? (size_t)0 : std::min<size_t>(r - i, even[l + r - i - 1]);
                while (i >= even[i] && i + even[i] + 1 < n && s[i - even[i]] == s[i + even[i] + 1])++even[i];
                if (i + even[i] > r)l = i - even[i] + 1, r = i + even[i];
            }
            *odd = 0;
            for (size_t i = 1, l = 0, r = 0; i < n; ++i){
                odd[i] = i >= r? (size_t)0 : std::min<size_t>(r - i, odd[l + r - i]);
                while (i >= odd[i] + 1 && i + odd[i] + 1 < n && s[i - odd[i] - 1] == s[i + odd[i] + 1])++odd[i];
                if (i + odd[i] > r)l = i - odd[i], r = i + odd[i];
            }
        }

}

时间复杂度 \(O(n)\)

例:QOJ # 8668. PKUSC2024 Day1T1 回文路径

字母矩阵 \(a_{1\sim 2,1\sim n}\),从任意位置开始,任意位置结束,每次只能向下或向右,求可以得到的回文串中最长的长度,\(n\le10^5\)

最优解有三种情况:

  1. 回文串长为奇数
  2. 回文串长度为偶数,且两个中心字符在同一行
  3. 回文串长度为偶数,且两个中心字符在不同行

其中前两种情况还要分为回文中心在第一行和第二行的两个子问题,两个子问题可以通过翻转合并为一种,因此只考虑回文中心在上一行的情况

两者都可以二分加哈希 或者使用 \(\operatorname{Manacher}\) 算法在回文中心所在一行尽量扩展,到极大后尝试右侧向下一行,显然这样最优

对于第三种情况,上下同时扩展即可

若用二分加哈希,则总时间复杂度 \(O(n\log n)\)代码

\(\text{Manacher}\) 算法可以优化常数,不清楚是否能够做到线性

参考

  1. 字符串乱学
posted @ 2025-03-17 19:45  Hstry  阅读(8)  评论(0)    收藏  举报