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

  • 相关阅读:
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十一)
    install ubuntu on Android mobile phone
    Mac OS, Mac OSX 与Darwin
    About darwin OS
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十)
    Linux下编译安装qemu和libvirt
    libvirt(virsh命令总结)
    深入浅出 kvm qemu libvirt
    自然语言交流系统 phxnet团队 创新实训 项目博客 (九)
    自然语言交流系统 phxnet团队 创新实训 项目博客 (八)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12400810.html
Copyright © 2011-2022 走看看