zoukankan      html  css  js  c++  java
  • [LeetCode] #5 Longest Palindromic Substring

    LeetCode 题目,原题链接 https://leetcode.com/problems/longest-palindromic-substring/。

    问题描述:找到字符串中最长回文子串。

    参考:http://articles.leetcode.com/longest-palindromic-substring-part-i/http://articles.leetcode.com/longest-palindromic-substring-part-ii/

    按照深入程度,解决方法如下。

    方法一

    猜想:回文就是正着读、反着读结果都是一样的字符串。那么把字符串给反过来,得到一个新字符串,比较原字符串与新字符串的最长相同子串就能得到结果。如S="fabcba"S="abcbaf"相同部分为"abcba",所以"abcba"S中的最长回文子串。

    但是,结果就是这么简单吗?这样想当然简单了。考虑,T="abcdba"T'="abdcba",它们最长相同子串是"ab",但是这东西不是回文。这个个例问题出在了将不同位置上的最长相同子串当做了回文。

    解决方法是考虑相同子串的位置,如在SS'自身长度为size=6,最长相同子串长度为len=5,子串在SS'中的位置分别为p=1p'=0,存在这样的关系(size-1)-(p+len-1)=p'

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    using namespace std;
    
    string longestPalindromeReverse(const string &s) {
        int size = s.size();
        string r = s;
        std::reverse(r.begin(), r.end());
        int longestLength = 1;
        int longestIndex = 0;
    
        for (int len = 1; len < size + 1; ++len) {
            for (int i = 0; i < size - len + 1; ++i) {
                string sub = s.substr(i, len);
                int p = i;
                int p_prime = r.find(sub);
    
                if (p_prime != string::npos && (size - 1) - (p + len - 1) == p_prime) {
                    if (longestLength < len) {
                        longestLength = len;
                        longestIndex = i;
                    }
                }
            }
        }
    
        return s.substr(longestIndex, longestLength);
    }
    
    int main() {
        cout << longestPalindromeReverse("zeusnilemacaronimaisanitratetartinasiaminoracamelinsuez") << endl;
    
        return 0;
    }
    

    这个方法的时间复杂度是 (O(n^2)),但是最内层循环有find函数,非常耗时间。所以被 LeetCode 以Time Limit Exceeded为理由拒绝了。

    空间复杂度是 (O(n))

    方法二

    动态规划的方法,动态规划的套路是用一个多维的数组保存中间结果,在计算时使用中间结果加速计算。所以动态规划的空间复杂度一般比较大。

    用一二维数组table保存中间结果,table[i][j]表示[i, j]的子串是否是回文。

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    using namespace std;
    
    string longestPalindromeDP(const string &s) {
        int longestBegin = 0;
        int longestLength = 1;
    
        bool table[1000][1000] = {false};
    
        for (int i = 0; i < s.size(); ++i) {
            table[i][i] = true;
        }
    
        for (int i = 0; i < s.size() - 1; ++i) {
            if (s[i] == s[i + 1]) {
                table[i][i + 1] = true;
                longestBegin = i;
                longestLength = 2;
            }
        }
    
        for (int len = 3; len < s.size() + 1; ++len) {
            for (int i = 0; i < s.size() - len + 1; ++i) {
                if (table[i + 1][i + len - 2] == true && s[i] == s[i + len - 1]) {
                    table[i][i + len - 1] = true;
                    longestLength = len;
                    longestBegin = i;
                }
            }
        }
    
        return s.substr(longestBegin, longestLength);
    }
    
    int main() {
        cout << longestPalindromeDP("zeusnilemacaronimaisanitratetartinasiaminoracamelinsuez") << endl;
    
        return 0;
    }
    

    方法三

    暴力搜索,找到回文所有可能的中间点,从中间点处向两侧扩展,搜索完所有的可能性,就能找到最长回文子串。

    注意,中间点可能是一个字符也有可能是两个字符,如abaabba

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    using namespace std;
    
    string expandArroundCenter(const string &s, int begin, int end) {
        int length = s.size();
        int l = begin - 1, r = end + 1;
    
        while (l >= 0 && r <= length - 1 && s[l] == s[r]) {
            --l;
            ++r;
        }
    
        return s.substr(l + 1, r - 1 - l);
    }
    
    string longestPalindromeSimple(const string &s) {
        int length = s.size();
        if (length == 0) return "";
        string longest = s.substr(0, 1);
        int longestLength = 1;
    
        int i = 0;
    
        for (i = 0; i < length - 1; ++i) {
            string p1 = expandArroundCenter(s, i, i);
            if (p1.size() > longestLength) {
                longestLength = p1.size();
                longest = p1;
            }
            if (s[i] == s[i + 1]) {
                string p2 = expandArroundCenter(s, i, i + 1);
                if (p2.size() > longestLength) {
                    longestLength = p2.size();
                    longest = p2;
                }
            }
        }
    
        return longest;
    }
    
    int main() {
        cout << longestPalindromeSimple("abacdgfdcaba") << endl;
        return 0;
    }
    

    方法四

    利用回文的对称性缩减计算时间。这个方法叫做Manacher’s Algorithm。维基百科上就是这个方法。

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    using namespace std;
    
    string preProcess(const string &s) {
        int n = s.size();
        if (n == 0) return "^$";
        string ret = "^";
        for (int i = 0; i < n; ++i) {
            ret.append("#").append(s.substr(i, 1));
        }
        ret.append("#$");
    
        return ret;
    }
    
    string longestPalindrome(const string &s) {
        string T = preProcess(s);
        int n = T.size();
        int C = 0, R = 0;
        int* P = new int[n];
    
        for (int i = 1; i < n - 1; ++i) {
            int i_mirror = 2 * C - i;
    
            P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0;
    
            while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) {
                ++P[i];
            }
    
            if (i + P[i] > R) {
                C = i;
                R = i + P[i];
            }
        }
    
        int longestLength = 0, longestIndex = 0;
        for (int i = 1; i < n - 1; ++i) {
            if (P[i] > longestLength) {
                longestLength = P[i];
                longestIndex = i;
            }
        }
    
        delete[] P;
    
        return s.substr((longestIndex - 1 - longestLength)/2, P[longestIndex]);
    }
    
    int main() {
        cout << longestPalindrome("abacdgfdcaba") << endl;
        return 0;
    }
    
  • 相关阅读:
    染色法判定二分图
    Kruskal算法求最小生成树
    Prim算法求最小生成树
    Floyd算法求多源最短路
    spfa判断负环
    java 线程的使用
    java IO基础
    数据库 EXISTS与NOT EXISTS
    数据库 何为相关查询和不相关查询?
    数据库的基础知识
  • 原文地址:https://www.cnblogs.com/JingeTU/p/6390929.html
Copyright © 2011-2022 走看看