头图

Manacher算法的讲解这里就不说了,网上文献很多,这里给三篇参考:

  1. 最长回文子串——Manacher 算法
  2. Manacher's ALGORITHM: O(n)时间求字符串的最长回文子串 (推荐)
  3. Longest Palindromic Substring Part II

推荐先看1,然后也许会像我一样有些迷糊,然后看2,看四五次基本就明白了,最后看3,一气呵成。

上代码:

/***********************************************************************************
 * 最长回文子串
 *
 * 系统环境:Deepin Linux 15.6 x64
 * 文件名称:longestPalindrome.cpp
 * 内容摘要:Manacher算法输出最长回文子串。
 * 其他说明:无
 * 当前版本:1.0
 * 作    者:xjer
 * 完成时期:2018.10.30
 *
 **********************************************************************************/
#include <bits/stdc++.h>

using namespace std;

string preProcess(string str, int len);
string manacher(string str, int len);

/***********************************************************************************
 * 功能描述:主函数
 * 输入参数:无
 * 输出参数:无
 * 返 回 值:无
 * 其他说明:无
 *
 * ***********************************************************************************/
int main()
{
    string str;
    cin >> str;
    int len = str.length() - 1;
    string lgstPalindrome = manacher(str, len);
    cout << lgstPalindrome << endl; // 输出最长回文子串
    return 0;
}

/***********************************************************************************
 * 功能描述:预处理函数
 * 输入参数:标准输入str,str的实际长度len
 * 输出参数:无
 * 返 回 值:预处理后的 tranStr字符串(奇数位)
 * 其他说明:1. 转换 str 例:str = "abba", tranStr = "$#a#b#b#a#&". $ 和 & 起哨兵作用,避免边界检查;
 *          2. string substr (size_t pos = 0, size_t len = npos)
               返回一个新建的初始化为string对象的子串的拷贝string对象。子串是,在字符位置 
               pos开始,跨越len个字符(或直到字符串的结尾,以先到者为准)对象的部分。
 *
 * ***********************************************************************************/
string preProcess(string str, int len)
{
    if (len == 0)
        return "$&";
    string ret = "$";
    for (int i = 0; i <= len; i++)
    {
        ret += "#" + str.substr(i, 1);
    }
    ret += "#&";
    //cout << ret << endl;
    return ret;
}

/***********************************************************************************
 * 功能描述:Manacher算法函数
 * 输入参数:标准输入str,str的实际长度len
 * 输出参数:无
 * 返 回 值:最长回文子串
 * 其他说明:算法时间复杂度 O(n)
 *
 * ***********************************************************************************/
string manacher(string str, int len)
{
    string tranStr = preProcess(str, len);
    int n = tranStr.length();
    /* 数组p[i]记录以字符tranStr[i]为中心的最长回文子串向左/右扩张的长度  
      (包括tranStr[i],也就是把该回文串“对折”以后的长度)*/
    int *p = new int[n];
    /* centerId 为已知的 {右边界最大} 的回文子串的中心,  
       border 则为centerId+p[centerId],也就是这个子串的右边界。*/
    int centerId = 0, border = 0;
    for (int i = 0; i < n - 1; i++)
    {
        // symI 是i关于centerId的对称点:symI = centerId - (i - centerId)
        int symI = 2 * centerId - i;
        // 对于 border <= i 的情况,无法对p[i]做更多的假设,只能p[i] = 1,然后再去匹配。
        p[i] = (border > i) ? min(p[symI], border - i) : 1;
        while (tranStr[i + p[i]] == tranStr[i - p[i]])
        {
            p[i]++;
        }

        if (p[i] + i > border)
        {
            centerId = i;
            border = p[i] + 1;
        }
    }

    //找出p[i]中最大的
    int maxLen = 0;      // 最长回文子串的长度
    int centerIndex = 0; // 对称中心
    for (int i = 0; i < n - 1; i++)
    {
        if (p[i] > maxLen)
        {
            maxLen = p[i];
            centerIndex = i;
        }
    }
    delete[] p;

    return str.substr((centerIndex - maxLen) / 2, maxLen);
}


xjer
4 声望0 粉丝

一世人生有炎凉,晨要担当,暮要担当。