最长回文子串的不同解法

给定一个字符串,返回该字符串的最长回文子串,回文也就是说 ,正着读和反着读是一样的。下面总结了几种求回文的方式:

方法1 : 很简单,枚举所有的区间 [i,j] ,查看该范围内是否是一个回文.

  时间复杂度 O(n^3),空间复杂度 O(1).

方法2: 方法1的时间复杂度太高,并且存在着大量的重复运算,可以使用DP来解,并且保存已经检查过的字符串的状态.

  时间复杂度: O(n^2),空间复杂度O(n^2).

这里存在两种DP的方法,是根据区间来进行DP,还是长度,不过都是大同小异,不改变整个算法的时间复杂度。

代码如下:

//dp 1
string LongestPalindrome(const string &s)
{
    const int n = s.size();
    if(n < 2) return s;
    bool f[n][n+1];

    fill_n(&f[0][0],n*(n+1),false);
    int start = 0, len = 1;

    f[0][0] = true;
    for(int i=0;i<n;++i)
    {
        f[i][0] = true;
        f[i][1] = true;
    }
    for(int i=n-2;i>=0;--i)
    {
        for(int j=2;j<=n && (i+j-1)<n;++j)
        {
            f[i][j] = f[i+1][j-2] && s[i] == s[i+j-1];
            if(f[i][j] && j > len) {start = i; len = j;}
        }
    }

    return s.substr(start,len);
}

//dp 2
string LongestPalindrome_dp2(const string &s)
{
    const int n = s.size();
    if(n < 2) return s;
    bool f[n][n];

    fill_n(&f[0][0],n*n,false);
    int start=0,len=1;
    f[0][0] = true;

    for(int i=0;i<n;++i)
        f[i][i] = true;

    for(int i = n-1 ; i >= 0; --i)
    {
        for(int j = i+1; j < n;++j)
        {
            if(j == i+1) f[i][j] = (s[i] == s[j]);
            else
                f[i][j] = f[i+1][j-1] && s[i] == s[j];
            if(f[i][j] && (j-i+1) > len) {start = i; len = j-i+1;}
        }
    }

    return s.substr(start,len);
}

方法3: 很直观的想法,以每一个字符串为中心,计算该字符串左右可以延伸的部分。注意处理长度为奇数和偶数的情况。

时间复杂度 : O(n^2) ,空间复杂度 : O(1)

//从中间往两端延伸(考虑奇数偶数的情况即可)

string LongestPalindrome_extend(const string &s)
{
    const int n = s.size();
    if(n < 2) return s;
    int low,high;
    int start=0,len=1;
    for(int i=1;i<n;++i)
    {
        //even
        low = i-1;
        high = i;
        while(low>=0&&high<n&&s[low]==s[high])
        {
            if(high-low+1>len)
            {
                start=low;
                len=high-low+1;
            }
            --low;++high;
        }
        //odd
        low = i-1;
        high = i+1;
        while(low>=0&&high<n&&s[low]==s[high])
        {
            if(high-low+1 > len)
            {
                start = low;
                len=high-low+1;
            }
            --low;++high;
        }
    }
    return s.substr(start,len);
}

方法3 :使用后缀数组的思想,将字符串s取s的逆,拼接在s的后面,也就是说 现在考察的字符串是 s#s‘,其中的#是额外的一个字符,s‘是s的逆串。求当前这个新拼接而成的字符串的后缀树组的最长公共前缀。

时间复杂度: O(n^2),空间复杂度 O(n^2)

//关于此方法还没想明白,暂不贴代码

方法4: manacher算法。参考 点击打开链接

时间复杂度: O(n),空间复杂度 O(n)

代码为:

//Manacher O(n)
string Manacher(const string &str)
{
    //add '#'
    string s = "$";
    for(auto a : str)
    {
        s += '#';
        s += a;
    }
    s += '#';
    cout << s << endl;
    const int n = s.size();
    vector<int> P(n,0);
    int right = -1, idx = -1;
    for(int i=1;i<n;++i)
    {
        P[i] = (right > i)? min(P[2*idx-i],right-i):1;
        while(s[i+P[i]] == s[i-P[i]])P[i]++;
        if(i+P[i]>right)
        {
            right = i + P[i];
            idx = i;
        }
    }
    auto pos = max_element(P.begin(),P.end());
    int len = *pos-1;
    string ret;
    int i = pos-P.begin();
    //print
    ret += s[i];
    cout << ret << endl;
    int k=1;
    while(len)
    {
        ret += s[i+k];
        ret = s[i-k]+ret;
        cout << ret << endl;
        ++k;
        --len;
    }
    //trim #
    string ret2;
    for(auto a :ret)
        if(a!='#')ret2 += a;
    return ret2;
}

上述代码均已验证正确,至于原理,全在代码中。

时间: 2024-12-20 18:09:09

最长回文子串的不同解法的相关文章

[转]最长回文子串——4种解法

题记: 最近刚研究了动态规划,感觉确实是算法思想中比较晦涩深奥的一种,解法2就是用动态规划,一般都是用数组记录尝试过的解法结果,为后续的解法提供剪枝.对于这道题目,解法1,解法3的思路比较简单易懂. 解法1:用两个for循环找出所有子串,第三个for循环用于判断该子串是否为回文,是回文则且比已找到的回文串长就替换,算法时间效率为O(n^3) 解法3:用for循环遍历字符串的每一个字符,每找到一个字符就以此为中心,往两边拓展,看左右字符串是否相等.但是回文有两种类型,一种为奇数,一种偶数,如下:

Manacher算法----最长回文子串

题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的.同时,奇数和偶数长度还要分别考虑. Manacher算法可以解决上述问题,并在O(n)时间复杂度内求出结果.下面我们来看一下Manacher算法. 首先,为了处理奇偶的问题,在每个字符的两边都插入一个特殊的符号,这样所有的奇数或偶数长度都转换为奇数长度.比

[C++]LeetCode: 99 Longest Palindromic Substring (最长回文子串)

题目:Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. 思路:题目要求的s的一个最长回文子串.暴力解决办法就是枚举所有的子串,再对每个子串进行回文判断.进行剪枝,我们考虑可以使用动态规划来避免重复的判

最长回文子串(转自:hihoCoder)

#1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:"小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?" 小Ho奇怪的问道:"什么叫做最长回文子串呢?" 小Hi回答道:"一个字符串中连续的一

转载:LeetCode:5Longest Palindromic Substring 最长回文子串

本文转自:http://www.cnblogs.com/TenosDoIt/p/3675788.html 题目链接 Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. 求字符串的最长回文子串 算法1:暴

#1032 : 最长回文子串

描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?” 小Ho奇怪的问道:“什么叫做最长回文子串呢?” 小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身

hiho#1032 : 最长回文子串 (manacher算法O(n)时间求字符串的最长回文子串 )

#1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:"小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?" 小Ho奇怪的问道:"什么叫做最长回文子串呢?" 小Hi回答道:"一个字符串中连续的一

[LeetCode]33. Longest Palindromic Substring最长回文子串

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. 解法一:考虑回文字符串paliStr的特征,分为字符串长度为奇偶两种情况:(1)paliStr.size()为奇数时,则从最中间的一个字符往两边扩展是

求最长回文子串:Manacher算法

主要学习自:http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html 问题描述:回文字符串就是左右对称的字符串,如:"abba",而最长回文子串则是字符串长度最长的回文子字符串,如"abbaca"的最长回文子串为"abba". 常规解法:显而易见采用嵌套循环的方式可以“暴力”结算出答案,其时间复杂度为O(n^2),而Manacher算法是一种更加