leetcode题目 寻找最长回文字串

题目: 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.
思路: 常见的解法有三种,时间复杂度分别为O(n3)、O(n2)、O(n)。本文分别实现这三类算法,贴出代码。

解法一: 暴力法,遍历所有可能的字串,判断是否为回文,若是回文记下最长字串长度。以下代码还做了些许优化,已知长度为len的回文字串,则长度小于len的字串直接跳过不做判断。

#include
#include
#include
#include 
#include 
using namespace std;

class Solution {
public:
    string longestPalindrome(string s) {
        int maxlen=0;
        auto start = s.begin();

        //长度小于maxlen的字串直接跳过
        for (auto i = s.begin(); i != s.end()&&s.end()-i>maxlen; ++i)   
        {
            int len = 0;
            for (auto j = s.end(); j > i&&(j-i>maxlen); --j)
            {
                //若是回文则记下长度,因为之前已经判断当前字串长度大于maxlen
                if (IsPalindromic(i, j))       
                {
                    maxlen = j - i;
                    start = i;
                }
            }
        }
        return string(start,start+maxlen);
    }
    bool IsPalindromic(string::iterator begin,string::iterator end)
    {
        if (end <= begin)
            return false;
        --end;
        while (begin < end &&*begin==*end)
        {
            begin++;
            end--;
        }
        if (begin >= end)
            return true;
        else
            return false;
    }
};

int main()
{
    string a;
    Solution s;
    while (cin >> a)
        cout << s.longestPalindrome(a);
}

本地测试结果,有两个测试用例,输出均正确
leetcode题目 寻找最长回文字串_第1张图片

将以上代码贴到leetcode上,结果却是时间超限。给出的测试用例可以看出很长,O(n3)的解法果然不行。
leetcode题目 寻找最长回文字串_第2张图片

解法二: 遍历字串,从某个中心开始向两边扩展寻找最长长度的回文串,此解法时间复杂度为O(n2)。注意要区分回文串为奇数长度和偶数长度的情况。另外为了方便边界控制,在向两边延伸时分别使用了string::iterator和string::reverse_iterator.这样向前和向后都可以通过判断是否小于s.rend或者小于s.end来作为边界条件。否则若使用s.begin和s.end作为边界条件则稍复杂。

#include
#include
#include
#include 
#include 
using namespace std;

class Solution {
public:
    string longestPalindrome(string s) {
        int maxlen = 0;
        int lenodd = 0;
        int leneven = 0;
        int len=0;
        auto start = s.begin();
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            //偶数长度的回文串最长半径
            leneven = IsPalindromicEven(i, s.end(), s.rend());
            //奇数长度的回文串最长半径
            lenodd = IsPalindromicOdd(i, s.end(), s.rend());
            if (2 * leneven > maxlen)
            {
                start = i + 1 - leneven;
                maxlen = 2 * leneven;
            }
            if (2 * lenodd-1 > maxlen)
            {
                start = i + 1 - lenodd;
                maxlen = 2 * lenodd - 1;
            }
        }
        return string(start, start + maxlen);
    }

    int IsPalindromicEven(string::iterator start, string::iterator end, string::reverse_iterator rend)
    {
        //这里fir指向start,sec指向start后边一位
        string::reverse_iterator fir = static_cast<string::reverse_iterator> (start+1);
        string::iterator sec = start+1;
        int len = 0;
        while (fir return len;
    }

    int IsPalindromicOdd(string::iterator start, string::iterator end,string::reverse_iterator rend )
    {
        //这里fir赋值时需要加1,反向迭代器才能和正向迭代器指向同一个字符
        string::reverse_iterator fir = static_cast<string::reverse_iterator> (start + 1);
        string::iterator sec = start;
        int len = 0;
        while (fir return len;
    }
};

int main()
{
    string a;
    Solution s;
    while (cin >> a)
        cout << s.longestPalindrome(a);
}

此种解法通过了leetcode所有用例,但是时间效率却不高,只击败了17%的代码,很明显还有更好的解法。
leetcode题目 寻找最长回文字串_第3张图片

解法3: Manacher算法,时间复杂度O(n).具体思路介绍网上有很多博客,本文代码则参考了左程云的书籍《IT名企算法与数据结构题目最优解》中关于Manacher算法的介绍。

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.size() == 0)
            return string();
        string temp;
        temp.push_back('#');
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            temp.push_back(*i);
            temp.push_back('#');
        }

        int *pArr = (int *) malloc(4*temp.size());
        memset(pArr, 0, 4*temp.size());
        int index = -1;
        int pr = -1;
        int maxlen = 0;
        int start = 0;

        for (int i = 0; i != temp.size(); ++i)
        {
            pArr[i] = pr>i ? min(pArr[2 * index - i], pr - i):1;
            while (i + pArr[i]size() && i - pArr[i]>-1)
            {
                auto a = temp[i - pArr[i]];
                a = temp[i - pArr[i]];
                if (temp[i + pArr[i]] == temp[i - pArr[i]])
                    pArr[i]++;
                else
                    break;
            }
            if (i + pArr[i] > pr)
            {
                pr = i + pArr[i];
                index = i;
            }
            if (pArr[i] > maxlen)
            {
                start = i;
                maxlen = max(maxlen, pArr[i]);
            }   
        }
        start /= 2;
        return string(s, start - ( maxlen - 1) / 2, maxlen-1);
    }
};

提交的leetcode后果然速度快了很多,击败了77%的代码。
leetcode题目 寻找最长回文字串_第4张图片

你可能感兴趣的:(C++)