溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

C++如何解決最長(zhǎng)回文子串問(wèn)題

發(fā)布時(shí)間:2022-10-14 15:41:53 來(lái)源:億速云 閱讀:104 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“C++如何解決最長(zhǎng)回文子串問(wèn)題”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“C++如何解決最長(zhǎng)回文子串問(wèn)題”文章能幫助大家解決問(wèn)題。

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"


這道題讓我們求最長(zhǎng)回文子串,首先說(shuō)下什么是回文串,就是正讀反讀都一樣的字符串,比如 "bob", "level", "noon" 等等。那么最長(zhǎng)回文子串就是在一個(gè)字符串中的那個(gè)最長(zhǎng)的回文子串。LeetCode 中關(guān)于回文串的題共有五道,除了這道,其他的四道為 Palindrome Number,Validate Palindrome,Palindrome Partitioning,Palindrome Partitioning II,我們知道傳統(tǒng)的驗(yàn)證回文串的方法就是兩個(gè)兩個(gè)的對(duì)稱(chēng)驗(yàn)證是否相等,那么對(duì)于找回文字串的問(wèn)題,就要以每一個(gè)字符為中心,像兩邊擴(kuò)散來(lái)尋找回文串,這個(gè)算法的時(shí)間復(fù)雜度是 O(n*n),可以通過(guò) OJ,就是要注意奇偶情況,由于回文串的長(zhǎng)度可奇可偶,比如 "bob" 是奇數(shù)形式的回文,"noon" 就是偶數(shù)形式的回文,兩種形式的回文都要搜索,對(duì)于奇數(shù)形式的,我們就從遍歷到的位置為中心,向兩邊進(jìn)行擴(kuò)散,對(duì)于偶數(shù)情況,我們就把當(dāng)前位置和下一個(gè)位置當(dāng)作偶數(shù)行回文的最中間兩個(gè)字符,然后向兩邊進(jìn)行搜索,參見(jiàn)代碼如下:

解法一:

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.size() < 2) return s;
        int n = s.size(), maxLen = 0, start = 0;
        for (int i = 0; i < n - 1; ++i) {
            searchPalindrome(s, i, i, start, maxLen);
            searchPalindrome(s, i, i + 1, start, maxLen);
        }
        return s.substr(start, maxLen);
    }
    void searchPalindrome(string s, int left, int right, int& start, int& maxLen) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            --left; ++right;
        }
        if (maxLen < right - left - 1) {
            start = left + 1;
            maxLen = right - left - 1;
        }
    }
};

我們也可以不使用子函數(shù),直接在一個(gè)函數(shù)中搞定,我們還是要定義兩個(gè)變量 start 和 maxLen,分別表示最長(zhǎng)回文子串的起點(diǎn)跟長(zhǎng)度,在遍歷s中的字符的時(shí)候,我們首先判斷剩余的字符數(shù)是否小于等于 maxLen 的一半,是的話表明就算從當(dāng)前到末尾到子串是半個(gè)回文串,那么整個(gè)回文串長(zhǎng)度最多也就是 maxLen,既然 maxLen 無(wú)法再變長(zhǎng)了,計(jì)算這些就沒(méi)有意義,直接在當(dāng)前位置 break 掉就行了。否則就要繼續(xù)判斷,我們用兩個(gè)變量 left 和 right 分別指向當(dāng)前位置,然后我們先要做的是向右遍歷跳過(guò)重復(fù)項(xiàng),這個(gè)操作很必要,比如對(duì)于 noon,i在第一個(gè)o的位置,如果我們以o為最中心往兩邊擴(kuò)散,是無(wú)法得到長(zhǎng)度為4的回文串的,只有先跳過(guò)重復(fù),此時(shí)left指向第一個(gè)o,right指向第二個(gè)o,然后再向兩邊擴(kuò)散。而對(duì)于 bob,i在第一個(gè)o的位置時(shí),無(wú)法向右跳過(guò)重復(fù),此時(shí) left 和 right 同時(shí)指向o,再向兩邊擴(kuò)散也是正確的,所以可以同時(shí)處理奇數(shù)和偶數(shù)的回文串,之后的操作就是更新 maxLen 和 start 了,跟上面的操作一樣,參見(jiàn)代碼如下: 

解法二:

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.size() < 2) return s;
        int n = s.size(), maxLen = 0, start = 0;
        for (int i = 0; i < n;) {
            if (n - i <= maxLen / 2) break;
            int left = i, right = i;
            while (right < n - 1 && s[right + 1] == s[right]) ++right;
            i = right + 1;
            while (right < n - 1 && left > 0 && s[right + 1] == s[left - 1]) {
                ++right; --left;
            }
            if (maxLen < right - left + 1) {
                maxLen = right - left + 1;
                start = left;
            }
        }
        return s.substr(start, maxLen);
    }
};

此題還可以用動(dòng)態(tài)規(guī)劃 Dynamic Programming 來(lái)解,根 Palindrome Partitioning II 的解法很類(lèi)似,我們維護(hù)一個(gè)二維數(shù)組 dp,其中 dp[i][j] 表示字符串區(qū)間 [i, j] 是否為回文串,當(dāng) i = j 時(shí),只有一個(gè)字符,肯定是回文串,如果 i = j + 1,說(shuō)明是相鄰字符,此時(shí)需要判斷 s[i] 是否等于 s[j],如果i和j不相鄰,即 i - j >= 2 時(shí),除了判斷 s[i] 和 s[j] 相等之外,dp[i + 1][j - 1] 若為真,就是回文串,通過(guò)以上分析,可以寫(xiě)出遞推式如下:

dp[i, j] = 1                                               if i == j

           = s[i] == s[j]                                if j = i + 1

           = s[i] == s[j] && dp[i + 1][j - 1]    if j > i + 1      

這里有個(gè)有趣的現(xiàn)象就是如果我把下面的代碼中的二維數(shù)組由 int 改為 vector<vector<int>> 后,就會(huì)超時(shí),這說(shuō)明 int 型的二維數(shù)組訪問(wèn)執(zhí)行速度完爆 std 的 vector 啊,所以以后盡可能的還是用最原始的數(shù)據(jù)類(lèi)型吧。

解法三:

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.empty()) return "";
        int n = s.size(), dp[n][n] = {0}, left = 0, len = 1;
        for (int i = 0; i < n; ++i) {
            dp[i][i] = 1;
            for (int j = 0; j < i; ++j) {
                dp[j][i] = (s[i] == s[j] && (i - j < 2 || dp[j + 1][i - 1]));
                if (dp[j][i] && len < i - j + 1) {
                    len = i - j + 1;
                    left = j;
                }
            }
        }
        return s.substr(left, len);
    }
};

最后要來(lái)的就是大名鼎鼎的馬拉車(chē)算法 Manacher"s Algorithm,這個(gè)算法的神奇之處在于將時(shí)間復(fù)雜度提升到了 O(n) 這種逆天的地步,而算法本身也設(shè)計(jì)的很巧妙,很值得我們掌握,參見(jiàn)我另一篇專(zhuān)門(mén)介紹馬拉車(chē)算法的博客 Manacher"s Algorithm 馬拉車(chē)算法,代碼實(shí)現(xiàn)如下:

解法四:

class Solution {
public:
    string longestPalindrome(string s) {
        string t ="$#";
        for (int i = 0; i < s.size(); ++i) {
            t += s[i];
            t += "#";
        }
        int p[t.size()] = {0}, id = 0, mx = 0, resId = 0, resMx = 0;
        for (int i = 1; i < t.size(); ++i) {
            p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
            while (t[i + p[i]] == t[i - p[i]]) ++p[i];
            if (mx < i + p[i]) {
                mx = i + p[i];
                id = i;
            }
            if (resMx < p[i]) {
                resMx = p[i];
                resId = i;
            }
        }
        return s.substr((resId - resMx) / 2, resMx - 1);
    }
};

關(guān)于“C++如何解決最長(zhǎng)回文子串問(wèn)題”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI