代碼隨想錄算法訓(xùn)練營第47天 | 第九章 動態(tài)規(guī)劃part13:647. 回文子串潮改、516.最長回文子序列向拆、動態(tài)規(guī)劃總結(jié)篇

第九章 動態(tài)規(guī)劃part13

詳細(xì)布置

647. 回文子串

文章講解

動態(tài)規(guī)劃
  1. 確定dp數(shù)組(dp table)以及下標(biāo)的含義
    dp[i] 和 dp[i-1] 烤咧,dp[i + 1] 看上去都沒啥關(guān)系。
    所以我們要看回文串的性質(zhì)抢呆。 如圖:


    image.png

    我們在判斷字符串S是否是回文煮嫌,那么如果我們知道 s[1],s[2]抱虐,s[3] 這個子串是回文的昌阿,那么只需要比較 s[0]和s[4]這兩個元素是否相同,如果相同的話恳邀,這個字符串s 就是回文串懦冰。
    那么此時我們是不是能找到一種遞歸關(guān)系,也就是判斷一個子字符串(字符串的下表范圍[i,j])是否回文谣沸,依賴于刷钢,子字符串(下表范圍[i + 1, j - 1])) 是否是回文。
    所以為了明確這種遞歸關(guān)系鳄抒,我們的dp數(shù)組是要定義成一位二維dp數(shù)組闯捎。
    布爾類型的dp[i][j]:表示區(qū)間范圍[i,j] (注意是左閉右閉)的子串是否是回文子串,如果是dp[i][j]為true许溅,否則為false瓤鼻。

  2. 確定遞推公式
    在確定遞推公式時,就要分析如下幾種情況贤重。
    整體上是兩種茬祷,就是s[i]與s[j]相等,s[i]與s[j]不相等這兩種并蝗。

  • 當(dāng)s[i]與s[j]不相等祭犯,dp[i][j]一定是false。
  • 當(dāng)s[i]與s[j]相等時滚停,有如下三種情況
    • 情況一:下標(biāo)i 與 j相同沃粗,同一個字符例如a,當(dāng)然是回文子串
    • 情況二:下標(biāo)i 與 j相差為1键畴,例如aa最盅,也是回文子串
    • 情況三:下標(biāo):i 與 j相差大于1的時候,例如cabac起惕,此時s[i]與s[j]已經(jīng)相同了涡贱,我們看i到j(luò)區(qū)間是不是回文子串就看aba是不是回文就可以了,那么aba的區(qū)間就是 i+1 與 j-1區(qū)間惹想,這個區(qū)間是不是回文就看dp[i + 1][j - 1]是否為true问词。
      以上三種情況分析完了,那么遞歸公式如下:
if (s[i] == s[j]) {
    if (j - i <= 1) { // 情況一 和 情況二
        result++;
        dp[i][j] = true;
    } else if (dp[i + 1][j - 1]) { // 情況三
        result++;
        dp[i][j] = true;
    }
}

result就是統(tǒng)計回文子串的數(shù)量嘀粱。
注意這里沒有列出當(dāng)s[i]與s[j]不相等的時候激挪,因為在下面dp[i][j]初始化的時候辰狡,就初始為false。

  1. dp數(shù)組如何初始化
    dp[i][j]可以初始化為true么灌灾? 當(dāng)然不行搓译,怎能剛開始就全都匹配上了悲柱。
    所以dp[i][j]初始化為false锋喜。

  2. 確定遍歷順序
    首先從遞推公式中可以看出,情況三是根據(jù)dp[i + 1][j - 1]是否為true豌鸡,在對dp[i][j]進(jìn)行賦值true的嘿般。

    dp[i + 1][j - 1] 在 dp[i][j]的左下角,如圖:
    image.png

    如果這矩陣是從上到下涯冠,從左到右遍歷炉奴,那么會用到?jīng)]有計算過的dp[i + 1][j - 1],也就是根據(jù)不確定是不是回文的區(qū)間[i+1,j-1]蛇更,來判斷了[i,j]是不是回文瞻赶,那結(jié)果一定是不對的。
    所以一定要從下到上派任,從左到右遍歷砸逊,這樣保證dp[i + 1][j - 1]都是經(jīng)過計算的。
  3. 舉例推導(dǎo)dp數(shù)組

    舉例掌逛,輸入:"aaa"师逸,dp[i][j]狀態(tài)如下:
    image.png
class Solution {
    public int countSubstrings(String s) {
        char[] chars = s.toCharArray();
        int len = s.length();
        boolean[][] dp = new boolean[len][len];
        int result = 0;
        for(int i = len - 1; i >= 0; i--){
            for(int j = i; j < len; j++){
                if(chars[i] == chars[j]){
                    if(j - i <= 1){ // 情況1 和情況2
                        dp[i][j] = true;
                        result++;
                    }else if(dp[i + 1][j - 1]){ //情況3
                        dp[i][j] = true;
                        result++;
                    }
                }
            }
        }
        return result;
    }
}
雙指針法

動態(tài)規(guī)劃的空間復(fù)雜度是偏高的,我們再看一下雙指針法豆混。
首先確定回文串篓像,就是找中心然后向兩邊擴(kuò)散看是不是對稱的就可以了。
在遍歷中心點的時候皿伺,要注意中心點有兩種情況员辩。
一個元素可以作為中心點,兩個元素也可以作為中心點鸵鸥。
那么有人同學(xué)問了奠滑,三個元素還可以做中心點呢。其實三個元素就可以由一個元素左右添加元素得到脂男,四個元素則可以由兩個元素左右添加元素得到养叛。
所以我們在計算的時候,要注意一個元素為中心點和兩個元素為中心點的情況宰翅。
這兩種情況可以放在一起計算弃甥,但分別計算思路更清晰

class Solution {
    public int countSubstrings(String s) {
        int result = 0;
        for(int i = 0; i < s.length(); i++){
            result += extend(s, i, i); //以i為中心
            result += extend(s, i, i + 1); //以i和i+1為中心
        }
        return result;
       
    }
    private int extend(String s, int i, int j){
        int res = 0;
        while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
            res++;
            i--; //注意這里是i-- j++
            j++;
        }
        return res;
    }
}

516.最長回文子序列

647. 回文子串,求的是回文子串汁讼,而本題要求的是回文子序列淆攻, 大家要搞清楚兩者之間的區(qū)別阔墩。
文章講解

思路

  • 本題是求回文子序列,回文子串是要連續(xù)的瓶珊,回文子序列可不是連續(xù)的啸箫!
  1. 確定dp數(shù)組(dp table)以及下標(biāo)的含義
    dp[i][j]:字符串s在[i, j]范圍內(nèi)最長的回文子序列的長度為dp[i][j]。

  2. 確定遞推公式
    在判斷回文子串的題目中伞芹,關(guān)鍵邏輯就是看s[i]與s[j]是否相同忘苛。

    如果s[i]與s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;
    image.png

如果s[i]與s[j]不相同唱较,說明s[i]和s[j]的同時加入 并不能增加[i,j]區(qū)間回文子序列的長度扎唾,那么分別加入s[i]、s[j]看看哪一個可以組成最長的回文子序列南缓。
加入s[j]的回文子序列長度為dp[i + 1][j]胸遇。
加入s[i]的回文子序列長度為dp[i][j - 1]。
那么dp[i][j]一定是取最大的汉形,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);


image.png
  1. dp數(shù)組如何初始化
  • 首先要考慮當(dāng)i 和j 相同的情況纸镊,從遞推公式:dp[i][j] = dp[i + 1][j - 1] + 2; 可以看出 遞推公式是計算不到 i 和j相同時候的情況。
  • 所以需要手動初始化一下概疆,當(dāng)i與j相同逗威,那么dp[i][j]一定是等于1的,即:一個字符的回文子序列長度就是1届案。
  • 其他情況dp[i][j]初始為0就行庵楷,這樣遞推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 中dp[i][j]才不會被初始值覆蓋。
  1. 確定遍歷順序
    從遞歸公式中楣颠,可以看出尽纽,dp[i][j] 依賴于 dp[i + 1][j - 1] ,dp[i + 1][j] 和 dp[i][j - 1]童漩,如圖:


    image.png

    所以遍歷i的時候一定要從下到上遍歷弄贿,這樣才能保證下一行的數(shù)據(jù)是經(jīng)過計算的。
    j可以正常從左向右遍歷矫膨。

  2. 舉例推導(dǎo)dp數(shù)組
    輸入s:"cbbd" 為例差凹,dp數(shù)組狀態(tài)如圖:


    image.png
class Solution {
    public int longestPalindromeSubseq(String s) {
        int len = s.length();
        int[][] dp = new int[len + 1][len + 1];
        for(int i = len - 1; i >= 0; i--){
            dp[i][i] = 1;
            for(int j = i + 1; j < len; j++){
                if(s.charAt(i) == s.charAt(j)){
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                }else{
                    dp[i][j] = Math.max(dp[i][j], Math.max(dp[i + 1][j], dp[i][j - 1]));
                }
            }
        }
        return dp[0][len - 1];
    }
}

動態(tài)規(guī)劃總結(jié)篇

https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E6%80%BB%E7%BB%93%E7%AF%87.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侧馅,隨后出現(xiàn)的幾起案子危尿,更是在濱河造成了極大的恐慌,老刑警劉巖馁痴,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谊娇,死亡現(xiàn)場離奇詭異,居然都是意外死亡罗晕,警方通過查閱死者的電腦和手機(jī)济欢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門赠堵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人法褥,你說我怎么就攤上這事茫叭。” “怎么了半等?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵揍愁,是天一觀的道長。 經(jīng)常有香客問我酱鸭,道長吗垮,這世上最難降的妖魔是什么垛吗? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任凹髓,我火速辦了婚禮,結(jié)果婚禮上怯屉,老公的妹妹穿的比我還像新娘蔚舀。我一直安慰自己,他們只是感情好锨络,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布赌躺。 她就那樣靜靜地躺著,像睡著了一般羡儿。 火紅的嫁衣襯著肌膚如雪礼患。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天掠归,我揣著相機(jī)與錄音缅叠,去河邊找鬼。 笑死虏冻,一個胖子當(dāng)著我的面吹牛肤粱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厨相,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼领曼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛮穿?” 一聲冷哼從身側(cè)響起庶骄,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎践磅,沒想到半個月后单刁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡音诈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年幻碱,在試婚紗的時候發(fā)現(xiàn)自己被綠了绎狭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡褥傍,死狀恐怖儡嘶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恍风,我是刑警寧澤蹦狂,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站朋贬,受9級特大地震影響凯楔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锦募,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一摆屯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧糠亩,春花似錦虐骑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至垂寥,卻和暖如春颠黎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滞项。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工狭归, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓖扑。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓唉铜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親律杠。 傳聞我的和親對象是個殘疾皇子潭流,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

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