算法导论—最长回文子串和子序列

华电北风吹
2016/3/5

回文串是指正序和逆序均相同的字符串。在回文子串的求解过程中,根据每个字符是否连续问题又可分为最长回文子串(绝对回文)和最长回文子序列(相对回文)。求解一个字符串中最长回文子串要求这个子串必须是连续的,而最长回文子序列则不要求这个子串是连续的,但是子串各个字符在原始串中的相对位置需要一致。例如对于字符串”character”,他的最长回文子串为”ara”,最长回文子序列则为”carac”。

一、最长回文子串
求解最长回文子串可以采用暴力破解(中心扩展)和动态规划(基于中心扩展,Manacher法)。
1、中心扩展
对于字符串的每个元素判断以他为中心的最长回文串长度。这里需要注意的是回文串分奇数串和偶数串,他们的代码可能会有稍微差异。中心扩展时间复杂度为 O(n2)

class Solution
{
public:
    string longestPalindrome(string s)
    {
        int n = s.length();
        int maxlength1 = 0, position1 = 0;
        for (int i = 1; i < n; i++)
        {
            int j;
            for (j = 1; j <= n / 2; j++)
            {
                if ((i - j < 0) || (i + j >= n) || (s[i - j] != s[i + j]))
                    break;
            }
            j--;
            if (j > maxlength1)
            {
                maxlength1 = j;
                position1 = i - maxlength1;
            }
        }
        int maxlength2 = 0, position2 = 0;
        for (int i = 1; i < n; i++)
        {
            if (s[i] == s[i - 1])
            {
                int p = 2;
                while ((i - p >= 0) && (i + p - 1 < n) && (s[i - p] == s[i + p - 1]))
                    p++;
                p--;
                if (p > maxlength2)
                {
                    maxlength2 = p;
                    position2 = i - p;
                }
            }
        }
        return maxlength1 >= maxlength2 ? (s.substr(position1, maxlength1 * 2 + 1)) : (s.substr(position2, maxlength2 * 2));
    }
};

2、动态规划(Manacher)
动态规划算法基于中心扩展。在对字符串中的每个字符串进行中心扩展的时候,如果这个字符在对前面的字符中心扩展的时候已经访问过,说明可以利用这个字符在的那个中心字符对称位置的回文串长度来提高当前字符串初始值(对称位置的值和边界两者最小值)。动态规划时间复杂度为 O(n)
这个方法有个很巧妙的字符串预处理,在任意两个字符串之间插入一个分割字符,这样可以将原始串中的奇回文串和偶回文串全部一一对应到新串中,这样方便后续处理(否则,参考上文)。预处理后所有的回文串都变成奇数串了,并且都以分割字符结束。

class Solution
{
public:
    string PreProcess(string str)
    {
        string s = "";
        int length = str.length();
        for (int i = 0; i<length; i++)
        {
            s.push_back('#');
            s.push_back(str[i]);
        }
        s.push_back('#');
        return s;
    }
    string longestPalindrome(string str)
    {
        string prestr = PreProcess(str);
        int rightBound = -1, pCenter = -1;
        int len = prestr.length();
        int *p = new int[len]();
        for (int i = 0; i < len; i++)
        {
            if (rightBound >= i)
                p[i] = min(rightBound - i + 1, p[2 * pCenter - i]);
            else
                p[i] = 1;
            while (i - p[i] >= 0 && i + p[i] < len && prestr[i - p[i]] == prestr[i + p[i]])
                p[i]++;
            if (i + p[i] - 1 > rightBound)
            {
                rightBound = p[i] + i - 1;
                pCenter = i;
            }
        }
        int maxIndex = 0;
        for (int i = 0; i<len; i++)
        {
            if (p[i]>p[maxIndex])
                maxIndex = i;
        }
        int maxlength = p[maxIndex] - 1;
        delete[] p;
        return str.substr((maxIndex - maxlength) / 2, maxlength);
    }
};

参考博客:http://www.cnblogs.com/heyonggang/p/3386724.html

二、最长回文子序列
最长回文子序列的动态规划思路跟LCS特别相似。空间复杂度和时间复杂度都同为 O(n2)

图片展示如下:
这里写图片描述

参考代码:

class Solution
{
public:
    int longestPalindrome(string str)
    {
        int n = str.length();
        vector<vector<int>> count(n, vector<int>(n, 0));
        for (int i = 0; i < n; i++)
            count[i][i] = 1;
        for (int i = n - 2; i >= 0; i--)
        {
            for (int j = i + 1; j < n; j++)
            {
                if (str[i] == str[j])
                {
                    if (j == i + 1)
                    {
                        count[i][j] = 2;
                    }
                    else
                    {
                        count[i][j] = count[i + 1][j - 1] + 2;
                    }
                }
                else
                {
                    count[i][j] = max(count[i + 1][j], count[i][j - 1]);
                }
            }
        }
        return count[0][n - 1];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值