zoukankan      html  css  js  c++  java
  • Manacher马拉车算法——解决最长回文子串问题

    大家可以先去看徐毅的《浅谈回文子串问题》。

    马拉车算法是一个用来求最长回文子串的线性算法,由Manacher在1975年发明。

    马拉车算法的代码复杂度较回文自动机低,对于一些问题已足够。回文自动机也有码量大,空间大,侧重于处理回文后缀等问题。

    这就好比SA和SAM各有自己的优势,回文自动机和Manacher在处理回文串问题也有自己的侧重。

    暴力求解最长回文子串的时间复杂度为$O(n^2)$,

    当然我们也可以用字符串哈希加二分做到$O(n imes log{n})$。

    而 $Manacher$算法可以进一步优化到$O(n)$。

    因为回文串的长度可以为奇数,也可以为偶数。如xibobix和xiboobix。

    为了统一我们在每个字符两边插入一个不再字符集内的字符如‘#’,变为#x#i#b#o#b#i#x#,#x#i#b#o#o#b#i#x#。这样我们就不用分情况讨论了。

    我们再引入一个新的与处理后的串等长的数组p,p_i​代表以i为中心最长的回文半径。

    p_i=1代表只有s这一个字符。

    如:

    # 1 # 2 # 2 # 1 # 2 # 2 #
    1 2 1 2 5 2 1 6 1 2 3 2 1

    知道了$p_i$有什么用呢?

    看上面那个例子,以第二个 '1' 为中心的回文子串 "#2#2#1#2#2#" 的半径是6,而未添加#号的回文子串为 "22122",长度是5,为半径减1。

    这是个巧合吗?

    我们再看以第三个 '#' 为中心的回文子串 "#1#2#2#1#" 的半径是5,而未添加#号的回文子串为 "1221",长度是4,为半径减1。

    总结一下:

    ```

    定理1 以i为中心的回文串的长度为$p_i-1$

    ```

    证明:

    (1) 先考虑以字符为中心的回文子串:

    如"#2#2#1#2#2#",其形式必为"#c#c……# c #……c#c#",$其半径即为原串中的半径 imes 2$。因为这是奇回文串,$所以原串长度=原串半径 imes 2 − 1$。所以原串长度等于$pi​−1$.

    (2) 以#为中心的回文子串:

    首先在原串中这是个偶回文串。

    其形式必为 "#c#c……#c # c#……c#c#",原串前一半的字符前都加一个#,长度为原串半径$ imes 2$即为原串长度。而在后面添加一个#变为新串半径所以原串长度等于新串半径$−1$即$p_i−1$。

    证毕

    我们现在知道了长度却不知道具体的子串。所以我们还需要知道一个回文串在原串中的开始位置。

    还是来看第二个$'1'$,它在处理后的串中排第$8$,半径为$6$,$8−6=2$,正是原串中的开始位置。

    我们再来看第三个$'#'$,它在处理后的串中排第$5$,半径为$5$,$5−5=0$,这可不行。我们要把中心往后移一位才行,所以们要在一开始加一个不相干的字符(如'@')。

    之后再研(da)究(biao)一下发现了如下规律:

    ```

    定理2 以i为中心的最长回文子串的开始位置为$frac{i-p_i}{2}+1$

    ```

    这个和定理1的证明及其相似,这里不再赘述。

    接下来我们看怎么求出$p$数组。

    我们可以先给$p$一个下界,再通过$s_{i-p}=s_{i+p}$来增加p。

    我们需要新增两个变量$mx, id$,$mx$代表现在已匹配的回文串右端点最远能到达的位置的下一位,$id$代表$mx$的中心点,显然$mx=id+p[id]$。我们还需要利用已知的$p_k$。

    接下来我们开始分类讨论。

    (1)$i geq mx$,这种情况我们对$[mx, i]$一无所知,只能把$p_i$设成$1$,再暴力匹配。

    (2)$i < mx$,这种情况我们又得分两种情况讨论

    我们找到$i$关于$id$的对称点,$id * 2 - i$,暂且记其为$j$。

    I $mx-i>p_j $

    显然以$j$为中心的最长回文串到不了$mx'$,这种情况我们不能确保$s_{i...mx}是否等于{s_{i-(mx-i)+1..i}}^r$,但我们可以肯定的是以$i$为中心的最长回文串的半径肯定不小于$p_j$。

    由于回文所以红色部分一定等于绿色部分的翻转。有因为红色部分也是回文所以红色部分的翻转等于红色部分。

    所以$p_i=p_j$。

    II $mx-i leq p_j$

    这种情况就比较遗憾,发现 $i+p_j-1 > mx$,这时我们的 $p_j$ 下界只能取到 $mx-i$。再暴力匹配

    我们发现我们不会再mx以内在进行暴力匹配,所以暴力匹配的范围是递增的,时间复杂度是$O(n)$。

    模板题:

    洛谷P3805 【模板】manacher算法

    模板题,直接上代码。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int n, n0, id, mx, res_len, res_center;
    int p[50000000];
    char tmp[11100000], s[50000000];
    void Manacher() {
        n0 = 0;
        s[++n0] = '$';
        for (int i = 1; i <= n; i++) s[++n0] = '#', s[++n0] = tmp[i];
        s[++n0] = '#';
        id = mx = res_len = res_center = 0;
        for (int i = 1; i <= n0; i++) {
            p[i] = mx > i ? min(mx - i, p[id * 2 - i]) : 1;
            while (s[i + p[i]] == s[i - p[i]]) ++p[i];
            if (mx < i + p[i]) mx = i + p[i], id = i;
            if (res_len < p[i]) res_len = p[i], res_center = i;
        }
    }
    int main() {
        scanf("%s", tmp + 1);
        n = strlen(tmp + 1);
        Manacher();
        cout << res_len - 1;
        return 0;
    }

    习题:

    因为时间有限,所以只有部分题目配有讲解,如有需要,请在评论区留言,谢谢!
    一些须知

    洛谷P1659 [国家集训队]拉拉队排练

    洛谷P4555 [国家集训队]最长双回文串

    洛谷P6216 回文匹配

    CF30E Tricky and Clever Password

    CF17E Palisection

  • 相关阅读:
    通过注册表找网站绝对路径
    西普学院Crypto之用户名和密码
    西普学院Crypto之一段奇怪的代码
    西普学院Crypto之先有鸡还是先有蛋
    西普学院Crypto之摩擦摩擦
    西普学院Crypto之凯撒是罗马共和国杰出的军事统帅
    各类文件的文件头标志[转]
    收藏的几个脑图
    同源策略详解及绕过[转]
    Python学习#1
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12400810.html
Copyright © 2011-2022 走看看