5. 最長回文子串 動(dòng)態(tài)規(guī)劃

給定一個(gè)字符串 s税朴,找到 s 中最長的回文子串宛渐。你可以假設(shè) s 的最大長度為 1000。

示例 1:

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個(gè)有效答案弓坞。
示例 2:

輸入: "cbbd"
輸出: "bb"

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作權(quán)歸領(lǐng)扣網(wǎng)絡(luò)所有饿凛。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處剖笙。

解法一:暴力解法

 public boolean isPalindromic(String s) {
        int len = s.length();
        for (int i = 0; i < len / 2; i++) {
            if (s.charAt(i) != s.charAt(len - i - 1)) {
                return false;
            }
        }
        return true;
    }

// 暴力解法
public String longestPalindrome(String s) {
    String ans = "";
    int max = 0;
    int len = s.length();
    for (int i = 0; i < len; i++)
        for (int j = i + 1; j <= len; j++) {
            String test = s.substring(i, j);
            if (isPalindromic(test) && test.length() > max) {
                ans = s.substring(i, j);
                max = Math.max(max, ans.length());
            }
        }
    return ans;
}

時(shí)間復(fù)雜度:兩層 for 循環(huán) O(n2),for 循環(huán)里邊判斷是否為回文请唱,O(n)弥咪,所以時(shí)間復(fù)雜度為 O(n3)。

空間復(fù)雜度:O(1)十绑,常數(shù)個(gè)變量聚至。

解法二 :最長公共子串

求兩個(gè)字符串的最長公共子串 參考:https://blog.csdn.net/u010397369/article/details/38979077

問題:有兩個(gè)字符串str和str2,求出兩個(gè)字符串中最長公共子串長度本橙。

比如:str=acbcbcef扳躬,str2=abcbced,則str和str2的最長公共子串為bcbce甚亭,最長公共子串長度為5贷币。

算法思路:
1、把兩個(gè)字符串分別以行和列組成一個(gè)二維矩陣亏狰。

2役纹、比較二維矩陣中每個(gè)點(diǎn)對(duì)應(yīng)行列字符中否相等,相等的話值設(shè)置為1暇唾,否則設(shè)置為0促脉。

3、通過查找出值為1的最長對(duì)角線就能找到最長公共子串策州。

針對(duì)于上面的兩個(gè)字符串我們可以得到的二維矩陣如下:

image.png

從上圖可以看到瘸味,str和str2共有5個(gè)公共子串,但最長的公共子串長度為5够挂。

為了進(jìn)一步優(yōu)化算法的效率旁仿,我們可以再計(jì)算某個(gè)二維矩陣的值的時(shí)候順便計(jì)算出來當(dāng)前最長的公共子串的長度,即某個(gè)二維矩陣元素的值由item[i][j]=1演變?yōu)閕tem[i][j]=1 +item[i-1][j-1]下硕,這樣就避免了后續(xù)查找對(duì)角線長度的操作了丁逝。修改后的二維矩陣如下:

image.png

代碼實(shí)現(xiàn)

        /**
     * 獲取兩個(gè)字符串最長公共子串長度
     * @param str   第一個(gè)字符串
     * @param str2  第二個(gè)字符串
     * @return  如果存在則返回最長公共子串長度,否則返回0
     */
    public static int getLCSLength(String str, String str2){
        char[] ary = str.toCharArray();
        char[] ary2 = str2.toCharArray();
        
        int[][] temp = new int[ary.length][ary2.length];    //聲明一個(gè)二維數(shù)組梭姓,存儲(chǔ)最長公共子串長度
        int length = 0; //最長公共子串長度
        for (int i = 0; i < ary.length; i++) {
            for (int j = 0; j < ary2.length; j++) {
                if(ary[i] == ary2[j]){
                    if(i > 0 && j > 0){
                        temp[i][j] = temp[i-1][j-1] + 1;
                    }else{
                        temp[i][j] = 1;
                    }
                    
                    if(temp[i][j] > length){    //當(dāng)前元素值大于最大公共子串長度
                        length = temp[i][j];
                    }
                }else{
                    temp[i][j] = 0;
                }
            }
        }
        return length;
    }

再看一個(gè)例子霜幼,S = "abc435cba",S’ = "abc534cba" 誉尖,最長公共子串是 "abc" 和 "cba" 罪既,但很明顯這兩個(gè)字符串都不是回文串。

所以我們求出最長公共子串后,并不一定是回文串琢感,我們還需要判斷該字符串倒置前的下標(biāo)和當(dāng)前的字符串下標(biāo)是不是匹配丢间。

比如 S = " caba ",S' = " abac " 驹针,S’ 中 aba 的下標(biāo)是 0 1 2 烘挫,倒置前是 3 2 1,和 S 中 aba 的下標(biāo)符合柬甥,所以 aba 就是我們需要找的饮六。當(dāng)然我們不需要每個(gè)字符都判斷,我們只需要判斷末尾字符就可以苛蒲。

image.png

首先 i 卤橄,j 始終指向子串的末尾字符。所以 j 指向的紅色的 a 倒置前的下標(biāo)是 beforeRev = length - 1 - j = 4 - 1 - 2 = 1臂外,對(duì)應(yīng)的是字符串首位的下標(biāo)窟扑,我們還需要加上字符串的長度才是末尾字符的下標(biāo),也就是 beforeRev + arr[ i ] [ j ] - 1 = 1 + 3 - 1 = 3漏健,因?yàn)?arr[ i ] [ j ] 保存的就是當(dāng)前子串的長度嚎货,也就是圖中的數(shù)字 3 。此時(shí)再和它與 i 比較蔫浆,如果相等厂抖,則說明它是我們要找的回文串。

之前的 S = "abc435cba"克懊,S' = "abc534cba" 忱辅,可以看一下圖示,為什么不符合谭溉。

image.png

當(dāng)前 j 指向的 c 墙懂,倒置前的下標(biāo)是 beforeRev = length - 1 - j = 9 - 1 - 2 = 6,對(duì)應(yīng)的末尾下標(biāo)是 beforeRev + arr[ i ] [ j ] - 1 = 6 + 3 - 1 = 8 扮念,而此時(shí) i = 2 损搬,所以當(dāng)前的子串不是回文串。

代碼的話,在上邊的基礎(chǔ)上,保存 maxLen 前判斷一下下標(biāo)匹不匹配就可以了拣凹。

public String longestPalindrome(String s) {
    if (s.equals(""))
        return "";
    String origin = s;
    String reverse = new StringBuffer(s).reverse().toString();
    int length = s.length();
    int[][] arr = new int[length][length];
    int maxLen = 0;
    int maxEnd = 0;
    for (int i = 0; i < length; i++)
        for (int j = 0; j < length; j++) {
            if (origin.charAt(i) == reverse.charAt(j)) {
                if (i == 0 || j == 0) {
                    arr[i][j] = 1;
                } else {
                    arr[i][j] = arr[i - 1][j - 1] + 1;
                }
            }
            /**********修改的地方*******************/
            if (arr[i][j] > maxLen) {
                int beforeRev = length - 1 - j;
                if (beforeRev + arr[i][j] - 1 == i) { //判斷下標(biāo)是否對(duì)應(yīng)
                    maxLen = arr[i][j];
                    maxEnd = i;
                }
                /*************************************/
            }
        }
    return s.substring(maxEnd - maxLen + 1, maxEnd + 1);
}

時(shí)間復(fù)雜度:兩層循環(huán),O(n2)颅悉。

空間復(fù)雜度:一個(gè)二維數(shù)組,O(n2)迁匠。

空間復(fù)雜度其實(shí)可以再優(yōu)化一下剩瓶。

image.png

我們分析一下循環(huán)驹溃,i = 0 ,j = 0延曙,1豌鹤,2 ... 8 更新一列,然后 i = 1 枝缔,再更新一列布疙,而更新的時(shí)候我們其實(shí)只需要上一列的信息,更新第 3 列的時(shí)候愿卸,第 1 列的信息是沒有用的拐辽。所以我們只需要一個(gè)一維數(shù)組就可以了。但是更新 arr [ i ] 的時(shí)候我們需要 arr [ i - 1 ] 的信息擦酌,假設(shè) a [ 3 ] = a [ 2 ] + 1,更新 a [ 4 ] 的時(shí)候菠劝, 我們需要 a [ 3 ] 的信息赊舶,但是 a [ 3 ] 在之前已經(jīng)被更新了,所以 j 不能從 0 到 8 赶诊,應(yīng)該倒過來笼平,a [ 8 ] = a [ 7 ] + 1,a [ 7 ] = a [ 6 ] + 1 , 這樣更新 a [ 8 ] 的時(shí)候用 a [ 7 ] 舔痪,用完后才去更新 a [ 7 ]寓调,保證了不會(huì)出錯(cuò)。

public String longestPalindrome(String s) {
    if (s.equals(""))
        return "";
    String origin = s;
    String reverse = new StringBuffer(s).reverse().toString();
    int length = s.length();
    int[] arr = new int[length];
    int maxLen = 0;
    int maxEnd = 0;
    for (int i = 0; i < length; i++)
        /**************修改的地方***************************/
        for (int j = length - 1; j >= 0; j--) {
        /**************************************************/
            if (origin.charAt(i) == reverse.charAt(j)) {
                if (i == 0 || j == 0) {
                    arr[j] = 1;
                } else {
                    arr[j] = arr[j - 1] + 1;
                }
            /**************修改的地方***************************/
            //之前二維數(shù)組锄码,每次用的是不同的列夺英,所以不用置 0 。
            } else {
                arr[j] = 0;
            }
            /**************************************************/
            if (arr[j] > maxLen) {
                int beforeRev = length - 1 - j;
                if (beforeRev + arr[j] - 1 == i) {
                    maxLen = arr[j];
                    maxEnd = i;
                }

            }
        }
    return s.substring(maxEnd - maxLen + 1, maxEnd + 1);
}

時(shí)間復(fù)雜度:O(n2)滋捶。

空間復(fù)雜度:降為 O(n)痛悯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市重窟,隨后出現(xiàn)的幾起案子载萌,更是在濱河造成了極大的恐慌,老刑警劉巖巡扇,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扭仁,死亡現(xiàn)場離奇詭異,居然都是意外死亡厅翔,警方通過查閱死者的電腦和手機(jī)乖坠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刀闷,“玉大人瓤帚,你說我怎么就攤上這事描姚。” “怎么了戈次?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵轩勘,是天一觀的道長。 經(jīng)常有香客問我怯邪,道長绊寻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任悬秉,我火速辦了婚禮澄步,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘和泌。我一直安慰自己村缸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布武氓。 她就那樣靜靜地躺著梯皿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪县恕。 梳的紋絲不亂的頭發(fā)上东羹,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音忠烛,去河邊找鬼属提。 笑死,一個(gè)胖子當(dāng)著我的面吹牛美尸,可吹牛的內(nèi)容都是我干的冤议。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼师坎,長吁一口氣:“原來是場噩夢啊……” “哼求类!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屹耐,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤尸疆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惶岭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寿弱,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年按灶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了症革。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸯旁,死狀恐怖噪矛,靈堂內(nèi)的尸體忽然破棺而出量蕊,到底是詐尸還是另有隱情,我是刑警寧澤艇挨,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布残炮,位于F島的核電站,受9級(jí)特大地震影響缩滨,放射性物質(zhì)發(fā)生泄漏势就。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一脉漏、第九天 我趴在偏房一處隱蔽的房頂上張望苞冯。 院中可真熱鬧,春花似錦侧巨、人聲如沸舅锄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皇忿。三九已至,卻和暖如春烘贴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撮胧。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工桨踪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芹啥。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓锻离,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墓怀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子汽纠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容