LeetCode算法練習(xí)——?jiǎng)討B(tài)規(guī)劃 DP

更多干貨就在我的個(gè)人博客 BlackBlog.tech 歡迎關(guān)注霜医!
也可以關(guān)注我的csdn博客:黑哥的博客
謝謝大家齿拂!

前面幾周完成了DFS、BFS的章節(jié)支子,一共大約40題左右创肥,今天開始練習(xí)動(dòng)態(tài)規(guī)劃。動(dòng)態(tài)規(guī)劃是一種將原問題拆分成子問題值朋,對子問題進(jìn)行求解叹侄,最終解決原文題的思想。動(dòng)態(tài)規(guī)劃適用于子問題不是獨(dú)立的情況昨登,也就是各子問題包含公共的子子問題趾代。
DP的關(guān)鍵在于尋找狀態(tài)轉(zhuǎn)移方程,即如何從前一狀態(tài)轉(zhuǎn)移到后一狀態(tài)丰辣,也可以理解為子問題的遞推公式撒强。

動(dòng)態(tài)規(guī)劃設(shè)計(jì)的兩種方法:
自頂向下(又稱記憶化搜索、備忘錄):基本上對應(yīng)著遞歸函數(shù)實(shí)現(xiàn)笙什,從大范圍開始計(jì)算飘哨,要注意不斷保存中間結(jié)果,避免重復(fù)計(jì)算
自底向上(遞推):從小范圍遞推計(jì)算到大范圍

其實(shí)前幾節(jié)搜索的問題很多也可以用DP的方式去求解琐凭。

給個(gè)目錄:
LeetCode63 不同路徑 II
LeetCode64 最小路徑和
LeetCode120 三角形最小路徑和
LeetCode91 解碼方法
LeetCode139 單詞拆分
LeetCode338 比特位計(jì)數(shù)
LeetCode357 計(jì)算各個(gè)位數(shù)不同的數(shù)字個(gè)數(shù)
LeetCode375 猜數(shù)字大小 II
LeetCode516 最長回文子序列
LeetCode647 回文子串

LeetCode63 不同路徑 II

題目

一個(gè)機(jī)器人位于一個(gè) m x n 網(wǎng)格的左上角 (起始點(diǎn)在下圖中標(biāo)記為“Start” )芽隆。
機(jī)器人每次只能向下或者向右移動(dòng)一步。機(jī)器人試圖達(dá)到網(wǎng)格的右下角(在下圖中標(biāo)記為“Finish”)统屈。
現(xiàn)在考慮網(wǎng)格中有障礙物胚吁。那么從左上角到右下角將會(huì)有多少條不同的路徑?
網(wǎng)格中的障礙物和空位置分別用 1 和 0 來表示愁憔。
說明:m 和 n 的值均不超過 100腕扶。

示例 1:

輸入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
輸出: 2
解釋:
3x3 網(wǎng)格的正中間有一個(gè)障礙物。
從左上角到右下角一共有 2 條不同的路徑:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

C++代碼

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(); //地圖高度
        int n = obstacleGrid[0].size(); ////地圖寬度
        vector<vector<int>> dp(m+1,vector<int>(n+1,0)); //初始化一個(gè)dp數(shù)組
        bool row_flag = true; //用來判斷第一行是否有1
        bool col_flag = true; //用來判斷第一列是否有1
        for(int i=0;i<n;i++){ //將第一行第一個(gè)障礙之前的所有點(diǎn) 標(biāo)記為1 表示有一種路徑可以到達(dá) 第一個(gè)障礙之后的所有點(diǎn)標(biāo)記為0 表示有0種路徑可達(dá)
            if(obstacleGrid[0][i]==1) row_flag = false; 
            if(row_flag) dp[0][i]=1;
            else dp[0][i]=0;
        }
        for(int i=0;i<m;i++){ //將第一列第一個(gè)障礙之前的所有點(diǎn) 標(biāo)記為1 表示有一種路徑可以到達(dá) 第一個(gè)障礙之后的所有點(diǎn)標(biāo)記為0 表示有0種路徑可達(dá)
            if(obstacleGrid[i][0]==1) col_flag = false;
            if(col_flag) dp[i][0]=1;
            else dp[i][0]=0;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){ //從[1][1]開始計(jì)算到達(dá)每一點(diǎn)的路徑數(shù)吨掌,如果該點(diǎn)不是障礙則計(jì)算半抱,狀態(tài)轉(zhuǎn)移方程為dp[i][j] = max(dp[i][j],dp[i-1][j]+dp[i][j-1])
                if(obstacleGrid[i][j]!=1) dp[i][j] = max(dp[i][j],dp[i-1][j]+dp[i][j-1]);
            }
        }
        return dp[m-1][n-1]; //返回右下角的值
    }
};

LeetCode64 最小路徑和

題目

給定一個(gè)包含非負(fù)整數(shù)的 m x n 網(wǎng)格脓恕,請找出一條從左上角到右下角的路徑,使得路徑上的數(shù)字總和為最小代虾。

說明:每次只能向下或者向右移動(dòng)一步进肯。

示例:

輸入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
輸出: 7
解釋: 因?yàn)槁窂?1→3→1→1→1 的總和最小。

C++

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        //dp數(shù)組表示從左上角到當(dāng)前點(diǎn)的最小路徑和
        int m = grid.size(); //矩陣的高度
        int n = grid[0].size(); //矩陣的寬度
        vector<vector<int>> dp(m+1,vector<int>(n+1,0)); //初始化一個(gè)dp數(shù)組
        dp[0][0] = grid[0][0]; //第一個(gè)點(diǎn)的最小路徑就是自己
        for(int i=1;i<n;i++){
            dp[0][i]=dp[0][i-1] + grid[0][i]; //初始化第一行 dp[0][i]=之前的路徑和+當(dāng)前路徑
        }
        for(int i=1;i<m;i++){
            dp[i][0] = dp[i-1][0] + grid[i][0]; //初始化第一列 dp[i][0]=之前路徑和+當(dāng)前路徑
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]; //狀態(tài)轉(zhuǎn)移方程 每一點(diǎn)最小路徑等與左點(diǎn) 上點(diǎn)中的較小值+當(dāng)前點(diǎn)的值
            }
        }
        return dp[m-1][n-1]; //返回右下角點(diǎn)
    }
};

體會(huì)

簡單的DP題棉磨。dp[i][j]表示從左上角到[i]j]的最小路徑和,首先初始化第一行與第一列学辱、第一行乘瓤、第一列每一個(gè)最小路徑都等于前一個(gè)點(diǎn)的最小路徑加上當(dāng)前路徑,從[1][1]點(diǎn)開始后策泣,狀態(tài)轉(zhuǎn)移方程為dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]衙傀,每一點(diǎn)最小路徑等與 左點(diǎn) 上點(diǎn) 中的較小值+當(dāng)前點(diǎn)的值。最后返回右下角的點(diǎn)萨咕。

LeetCode120 三角形最小路徑和

題目

給定一個(gè)三角形统抬,找出自頂向下的最小路徑和。每一步只能移動(dòng)到下一行中相鄰的結(jié)點(diǎn)上危队。

例如聪建,給定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)茫陆。

說明:

如果你可以只使用 O(n) 的額外空間(n 為三角形的總行數(shù))來解決這個(gè)問題金麸,那么你的算法會(huì)很加分。

C++代碼

int minimumTotal(vector<vector<int>> triangle) {
    int width = triangle[triangle.size()-1].size(); //三角形的最大寬度
    int depth = triangle.size(); //三角形最大高度
    vector<vector<int>> dp(triangle.size(),vector<int>(triangle[triangle.size()-1].size(),0));  //初始化一個(gè)dp數(shù)組
    //dp[i][j]表示從底部開始向上計(jì)算 第i行j列的最小路徑和
    for(int i=0;i<width;i++){ 
        dp[depth-1][i] = triangle[depth-1][i]; //初始化最后一行的值 最后一行的最小路路徑就是三角形的路徑
    }
    for(int i=depth-2;i>=0;i--){ //從倒數(shù)第二行向上開始計(jì)算
        for(int j=0;j<triangle[i].size();j++){//遍歷一行中的所有數(shù)據(jù)
            dp[i][j] = triangle[i][j] + min(dp[i+1][j],dp[i+1][j+1]);//狀態(tài)轉(zhuǎn)移方程 選出底下一行中較小的數(shù)字 將其與當(dāng)前三角形內(nèi)的值相加
        }
    }
    return dp[0][0]; //最終的結(jié)果
}

體會(huì)

一道基礎(chǔ)的DP題簿盅,從底向上進(jìn)行計(jì)算挥下,dp[i][j]表示第i行j列的最小路徑和。從倒數(shù)第二行向上開始循環(huán)桨醋,狀態(tài)轉(zhuǎn)移方程為dp[i][j] = triangle[i][j] + min(dp[i+1][j],dp[i+1][j+1]);即選出底下一行中較小的數(shù)字棚瘟,將其與當(dāng)前三角形內(nèi)的值相加。最終dp[0][0]為最終的結(jié)果喜最。

LeetCode91 解碼方法

題目

一條包含字母 A-Z 的消息通過以下方式進(jìn)行了編碼:

'A' -> 1
'B' -> 2
...
'Z' -> 26

給定一個(gè)只包含數(shù)字的非空字符串偎蘸,請計(jì)算解碼方法的總數(shù)。

示例 1:

輸入: "12"
輸出: 2
解釋: 它可以解碼為 "AB"(1 2)或者 "L"(12)返顺。

示例 2:

輸入: "226"
輸出: 3
解釋: 它可以解碼為 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 禀苦。

C++代碼

class Solution {
public:
    int numDecodings(string s) {
        vector<int> dp(s.size()+1,0);
        dp[0]=1;
        //dp[i]表示前i位的解碼方式為dp[i]種
        for(int i=1;i<dp.size();i++){
            if(s[i-1]=='0') dp[i-1] = 0; //0 不能單獨(dú)解碼,dp[i-1]賦值為0
            if(s[i-2]=='1' || (s[i-2]=='2'&&s[i-1]<'7')) dp[i] = dp[i-1]+dp[i-2]; //如果十位為1或者十位為2 個(gè)位<7 遂鹊,證明這個(gè)數(shù)字可以按照兩位數(shù)進(jìn)行分解
            else dp[i] = dp[i-1]; // 如果不滿足分解情況振乏,直接dp[i] = dp[i-1]
        }
        return dp[s.size()]; //返回dp[size]為最終的結(jié)果
    }
};

體會(huì)

斐波納切問題,建立一個(gè)dp數(shù)組秉扑,dp[i]表示前i為的解碼方式為dp[i]種慧邮。初值賦值為1调限,表示至少都有一種解碼方式,即逐個(gè)分割误澳。之后對后面的數(shù)字進(jìn)行循環(huán)耻矮,如果遇到0,0不能單獨(dú)解碼忆谓,將dp[i-1]設(shè)置為0裆装;如果遇到1開頭,或者2開頭且后一位小于7的數(shù)字倡缠,證明其可以看成一個(gè)兩位數(shù)哨免,則多一種解碼方式,dp[i] = dp[i-1]+1; 最終輸出dp[s.size()]表示最終的結(jié)果昙沦。

LeetCode139 單詞拆分

題目

給定一個(gè)非空字符串 s 和一個(gè)包含非空單詞列表的字典 wordDict琢唾,判定 s 是否可以被空格拆分為一個(gè)或多個(gè)在字典中出現(xiàn)的單詞。

說明:

拆分時(shí)可以重復(fù)使用字典中的單詞盾饮。
你可以假設(shè)字典中沒有重復(fù)的單詞采桃。
示例 1:

輸入: s = "leetcode", wordDict = ["leet", "code"]
輸出: true
解釋: 返回 true 因?yàn)?"leetcode" 可以被拆分成 "leet code"。

示例 2:

輸入: s = "applepenapple", wordDict = ["apple", "pen"]
輸出: true
解釋: 返回 true 因?yàn)?"applepenapple" 可以被拆分成 "apple pen apple"丘损。
     注意你可以重復(fù)使用字典中的單詞普办。

示例 3:

輸入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
輸出: false

C++代碼

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        if(s.size()<0 || wordDict.empty()) return false; //如果字符串長度小于0,或者字典為空号俐,則返回空值泌豆。
        unordered_set<string> dict; //將wordDict轉(zhuǎn)換成一個(gè)unordered_set,方便快速查詢
        for(auto key:wordDict) dict.insert(key); //將wordDict轉(zhuǎn)化為dict
        vector<bool> dp(s.size()+1,false); //創(chuàng)建一個(gè)dp數(shù)組 dp[i]表示前i個(gè)字符是否可以被拆分
        dp[0] = true; //初始值為true
        for(int i=1;i<=s.size();i++){ //前i個(gè)數(shù)字 字符串的終點(diǎn)
            for(int j=0;j<=i;j++){ //字符串的起點(diǎn)
                string new_str = s.substr(j,i-j); //i-j表示字符串的長度 j表示字符串的起點(diǎn)
                if(dp[j] && dict.count(new_str)) { //dp[j]表示前j個(gè)字符是否可以被劃分(相當(dāng)于起點(diǎn)前的字符串被劃分) new_str是新的字符串 如果前面的字符串可以被劃分且new_str可以被劃分,則字符串前i個(gè)字符可以被劃分吏饿。
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.size()];
    }

};

體會(huì)

動(dòng)態(tài)規(guī)劃問題踪危,判斷一個(gè)字符串是否可以劃分,只需要將其不斷拆分為小的字符串猪落,判斷其是否可以劃分即可贞远。dp[i]表示前i個(gè)字符是否可以劃分。i從1到size進(jìn)行循環(huán)笨忌,j從0到i進(jìn)行循環(huán)蓝仲,i可以看成是子串的終點(diǎn),j可以看成是子串的起點(diǎn)官疲,如果dp[j]為true袱结,表示前j個(gè)字符是可以劃分的,如果當(dāng)前的new_str可以劃分途凫,那么證明前i個(gè)字符是可以劃分的垢夹,依次類推,循環(huán)整個(gè)字符串维费。

LeetCode338 比特位計(jì)數(shù)

題目

給定一個(gè)非負(fù)整數(shù) num果元。對于 0 ≤ i ≤ num 范圍中的每個(gè)數(shù)字 i 促王,計(jì)算其二進(jìn)制數(shù)中的 1 的數(shù)目并將它們作為數(shù)組返回。

示例 1:

輸入: 2
輸出: [0,1,1]

示例 2:

輸入: 5
輸出: [0,1,1,2,1,2]

進(jìn)階:

給出時(shí)間復(fù)雜度為O(n*sizeof(integer))的解答非常容易而晒。但你可以在線性時(shí)間O(n)內(nèi)用一趟掃描做到嗎蝇狼?
要求算法的空間復(fù)雜度為O(n)。
你能進(jìn)一步完善解法嗎倡怎?要求在C++或任何其他語言中不使用任何內(nèi)置函數(shù)(如 C++ 中的 __builtin_popcount)來執(zhí)行此操作迅耘。

C++代碼

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> dp(num+1,0);
        for(int i=1;i<=num;i++)
            dp[i] = dp[i>>1]+(i&1);//1001 可以看成是 100中1的個(gè)數(shù) + 最后一位是否為1 
        return dp;
    }
};

體會(huì)

這個(gè)題的關(guān)鍵就在于狀態(tài)轉(zhuǎn)移方程驯妄。1001中1的個(gè)數(shù)可以看作是100中1的個(gè)數(shù)加上最后一位是否為1氯迂,所以狀態(tài)狀態(tài)轉(zhuǎn)移方程為dp[i] = dp[i>>1]+(i&1),若推導(dǎo)過程中用十進(jìn)制去思考宿接,很難觀察到該狀態(tài)轉(zhuǎn)移方程焦匈。

LeetCode357 計(jì)算各個(gè)位數(shù)不同的數(shù)字個(gè)數(shù)

題目

給定一個(gè)非負(fù)整數(shù) n,計(jì)算各位數(shù)字都不同的數(shù)字 x 的個(gè)數(shù)昵仅,其中 0 ≤ x < 10n 缓熟。

示例:

輸入: 2
輸出: 91 
解釋: 答案應(yīng)為除去 11,22,33,44,55,66,77,88,99 外,在 [0,100) 區(qū)間內(nèi)的所有數(shù)字摔笤。

C++代碼

int countNumbersWithUniqueDigits(int n) {
    if(n==0) return 0; //0 1 2 這三種情況直接輸出
    if(n==1) return 10;
    if(n==2) return 91;
    if(n>2){
        int sum = 91;//兩位數(shù)中的各個(gè)位不同的數(shù)字有91個(gè)
        for(int j=3;j<=n;j++){
            int bit = 9; //第二位有9種情況
            int small_sum = 9; //第一位有9種情況
            for(int i=0;i<j-1;i++){
                small_sum = small_sum*bit; //各個(gè)位的情況相乘
                bit--; //從第二位開始往后每位可選擇的數(shù)字少一個(gè)
            }
            sum =  sum+small_sum;//統(tǒng)計(jì)所有數(shù)據(jù)
        }
        return sum;
    }
}

體會(huì)

組合排列問題够滑。當(dāng)n=2時(shí):十位有9種選擇,個(gè)位有9種選擇吕世;當(dāng)n=3時(shí)彰触,百位有9種選擇,十位有9種選擇命辖,個(gè)位有8種選擇况毅;當(dāng)n=4時(shí),千位有9種選擇尔艇,百位有9種選擇尔许,十位有8種選擇,個(gè)位有7種選擇终娃。
所以:
n=2 9x9
n=3 9x9x8
n=4 9x9x8x7
以此類推味廊,n在10以上后,必定有重復(fù)棠耕。

LeetCode375 猜數(shù)字大小 II

題目

我們正在玩一個(gè)猜數(shù)游戲余佛,游戲規(guī)則如下:

我從 1 到 n 之間選擇一個(gè)數(shù)字,你來猜我選了哪個(gè)數(shù)字窍荧。

每次你猜錯(cuò)了辉巡,我都會(huì)告訴你,我選的數(shù)字比你的大了或者小了搅荞。

然而红氯,當(dāng)你猜了數(shù)字 x 并且猜錯(cuò)了的時(shí)候框咙,你需要支付金額為 x 的現(xiàn)金。直到你猜到我選的數(shù)字痢甘,你才算贏得了這個(gè)游戲喇嘱。

示例:

n = 10, 我選擇了8.

第一輪: 你猜我選擇的數(shù)字是5,我會(huì)告訴你塞栅,我的數(shù)字更大一些者铜,然后你需要支付5塊。
第二輪: 你猜是7放椰,我告訴你作烟,我的數(shù)字更大一些,你支付7塊砾医。
第三輪: 你猜是9拿撩,我告訴你,我的數(shù)字更小一些如蚜,你支付9塊压恒。

游戲結(jié)束。8 就是我選的數(shù)字错邦。

你最終要支付 5 + 7 + 9 = 21 塊錢探赫。

給定 n ≥ 1,計(jì)算你至少需要擁有多少現(xiàn)金才能確保你能贏得這個(gè)游戲撬呢。

C++代碼

class Solution {
public:
    int getMoneyAmount(int n) {
        vector<vector<int>> dp(n+1,vector<int>(n+1,0));
        //極小極大問題
        if(n<=2) return n-1;
        for(int i=2;i<=n;i++){ //i表示終點(diǎn)
            for(int j=i-1;j>0;j--){ //j表示起點(diǎn)
                int global = 999; //求最后的極小值 先初始化一個(gè)極大值
                int local = 0; //需要找到每一個(gè)子序列的極大值
                for(int k=j+1;k<i;k++){
                    local = k + max(dp[k+1][i],dp[j][k-1]); //狀態(tài)轉(zhuǎn)移方程
                    global = min(local,global); //最后取i j之間的最小值
                }
                dp[j][i] = j+1==i?j:global;
            }
        }
        return dp[1][n];//整個(gè)序列
    }
};

體會(huì)

這個(gè)題目的技巧性還是比較高的伦吠,狀態(tài)轉(zhuǎn)移方法比較難以尋找。
那么我們需要建立一個(gè)二維的dp數(shù)組魂拦,其中dp[i][j]表示從數(shù)字i到j(luò)之間猜中任意一個(gè)數(shù)字最少需要花費(fèi)的錢數(shù)毛仪,那么我們需要遍歷每一段區(qū)間[j, i],維護(hù)一個(gè)全局最小值global_min變量晨另,然后遍歷該區(qū)間中的每一個(gè)數(shù)字潭千,計(jì)算局部最大值local_max = k + max(dp[j][k - 1], dp[k + 1][i]),這個(gè)正好是將該區(qū)間在每一個(gè)位置都分為兩段借尿,然后取當(dāng)前位置的花費(fèi)加上左右兩段中較大的花費(fèi)之和為局部最大值刨晴,為啥要取兩者之間的較大值呢,因?yàn)槲覀円猚over所有的情況路翻,就得取最壞的情況狈癞。然后更新全局最小值,最后在更新dp[j][i]的時(shí)候看j和i是否是相鄰的茂契,相鄰的話賦為i蝶桶,否則賦為global_min。這里為啥又要取較小值呢掉冶,因?yàn)閐p數(shù)組是求的[j, i]范圍中的最低cost真竖,比如只有兩個(gè)數(shù)字1和2脐雪,那么肯定是猜1的cost低。

如果只有一個(gè)數(shù)字恢共,那么我們不用猜战秋,cost為0。
如果有兩個(gè)數(shù)字讨韭,比如1和2脂信,我們猜1,即使不對透硝,我們cost也比猜2要低狰闪。
如果有三個(gè)數(shù)字1,2濒生,3埋泵,那么我們就先猜2,根據(jù)對方的反饋罪治,就可以確定正確的數(shù)字秋泄,所以我們的cost最低為2。
如果有四個(gè)數(shù)字1规阀,2,3瘦麸,4谁撼,那么情況就有點(diǎn)復(fù)雜了,那么我們的策略是用k來遍歷所有的數(shù)字滋饲,然后再根據(jù)k分成的左右兩個(gè)區(qū)間厉碟,取其中的較大cost加上k。
當(dāng)k為1時(shí)屠缭,左區(qū)間為空箍鼓,所以cost為0,而右區(qū)間2呵曹,3款咖,4,根據(jù)之前的分析應(yīng)該取3奄喂,所以整個(gè)cost就是1+3=4铐殃。
當(dāng)k為2時(shí),左區(qū)間為1跨新,cost為0富腊,右區(qū)間為3,4域帐,cost為3赘被,整個(gè)cost就是2+3=5是整。
當(dāng)k為3時(shí),左區(qū)間為1民假,2浮入,cost為1,右區(qū)間為4阳欲,cost為0舵盈,整個(gè)cost就是3+1=4。
當(dāng)k為4時(shí)球化,左區(qū)間1秽晚,2,3筒愚,cost為2赴蝇,右區(qū)間為空,cost為0巢掺,整個(gè)cost就是4+2=6句伶。
引自:https://blog.csdn.net/xuxuxuqian1/article/details/81636415

LeetCode516 最長回文子序列

題目

給定一個(gè)字符串s,找到其中最長的回文子序列陆淀】加啵可以假設(shè)s的最大長度為1000。

示例 1:

輸入:

"bbbab"
輸出:

4
一個(gè)可能的最長回文子序列為 "bbbb"轧苫。

示例 2:

輸入:

"cbbd"
輸出:

2
一個(gè)可能的最長回文子序列為 "bb"楚堤。

C++代碼

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        string t = s;
        reverse(t.begin(),t.end()); //新建一個(gè)字符串 取反向 尋找公共子序列 就是最長回文串
        return lcs(t,s);
    }
    int lcs(string src,string dst){
        int n = src.size();
        vector<vector<int>> dp(n+1,vector<int>(n+1,0)); //新建一個(gè)dp數(shù)組
        //dp數(shù)組初始化 第一行 第一列初始化為0
        for(int i=0;i<=n;i++) {
            dp[0][i] = 0;
            dp[i][0] = 0;
        } 
        
        for(int i=1;i<=n;i++){ //從第二個(gè)元素開始遍歷
            for(int j=1;j<=n;j++){
                if(src[i-1]==dst[j-1]) dp[i][j] = dp[i-1][j-1]+1; //狀態(tài)轉(zhuǎn)移方程
                else {
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]); 
                }
            }
        }
        return dp[n][n];
    }
};

體會(huì)

判斷最長回文子串,只需要將一個(gè)字符串反轉(zhuǎn)后與原字符串求LCS含懊,就是最長回文子串身冬。
LCS的狀態(tài)轉(zhuǎn)移方程比較簡單。
0 if i=0 or j=0
c[i,j] = c[i-1,j-1]+1 if i,j>0 and xi = yj
max(c[i,j-1],c[i-1,j]) if i,j>0 and xi != yj
記住就好

LeetCode647 回文子串

題目

給定一個(gè)字符串岔乔,你的任務(wù)是計(jì)算這個(gè)字符串中有多少個(gè)回文子串酥筝。

具有不同開始位置或結(jié)束位置的子串,即使是由相同的字符組成雏门,也會(huì)被計(jì)為是不同的子串嘿歌。

示例 1:

輸入: "abc"
輸出: 3
解釋: 三個(gè)回文子串: "a", "b", "c".

示例 2:

輸入: "aaa"
輸出: 6
說明: 6個(gè)回文子串: "a", "a", "a", "aa", "aa", "aaa".

注意:

輸入的字符串長度不會(huì)超過1000。

C++代碼

class Solution {
public:
    int help(string str,int left,int right){
        int count = 0;
        while(str[left]==str[right] && left>=0 && right<=str.size()-1){ //如果str[left]==str[right] 且沒有越界的情況下 向左右伸展 并記數(shù)
            count++; //記數(shù)
            left--; //向左伸展
            right++; //向右伸展
        }
        return count;
    }

    int countSubstrings(string s) {
        int sum = 0; //用來記數(shù)
        for(int i=0;i<s.size();i++){
            sum+= help(s,i,i); //長度為奇數(shù)的回文數(shù)
            sum+= help(s,i,i+1); //長度為偶數(shù)的回文數(shù)
        }
        return sum;
    }
};

體會(huì)

簡單題剿配,回文數(shù)有兩種情況搅幅,一種是長度為奇數(shù)的回文數(shù),一種是長度為偶數(shù)的回文數(shù)呼胚,找到中間值茄唐,向兩邊伸展同時(shí)進(jìn)行判斷就可以。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沪编,隨后出現(xiàn)的幾起案子呼盆,更是在濱河造成了極大的恐慌,老刑警劉巖蚁廓,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件访圃,死亡現(xiàn)場離奇詭異,居然都是意外死亡相嵌,警方通過查閱死者的電腦和手機(jī)腿时,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饭宾,“玉大人批糟,你說我怎么就攤上這事】疵” “怎么了徽鼎?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弹惦。 經(jīng)常有香客問我否淤,道長,這世上最難降的妖魔是什么棠隐? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任石抡,我火速辦了婚禮,結(jié)果婚禮上助泽,老公的妹妹穿的比我還像新娘汁雷。我一直安慰自己,他們只是感情好报咳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挖藏,像睡著了一般暑刃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上膜眠,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天岩臣,我揣著相機(jī)與錄音,去河邊找鬼宵膨。 笑死架谎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辟躏。 我是一名探鬼主播谷扣,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了会涎?” 一聲冷哼從身側(cè)響起裹匙,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎末秃,沒想到半個(gè)月后概页,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡练慕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年惰匙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铃将。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡项鬼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出麸塞,到底是詐尸還是另有隱情秃臣,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布哪工,位于F島的核電站奥此,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雁比。R本人自食惡果不足惜稚虎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偎捎。 院中可真熱鬧蠢终,春花似錦、人聲如沸茴她。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丈牢。三九已至祭钉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間己沛,已是汗流浹背慌核。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留申尼,地道東北人垮卓。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像师幕,于是被迫代替她去往敵國和親粟按。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

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

  • 怎么提高學(xué)生的寫作素質(zhì)一直是語文老師頭痛的問題?剛接手這一屆學(xué)生的時(shí)候钾怔,他們害怕寫作碱呼,討厭寫作,敷衍寫作宗侦。為了激發(fā)...
    湯利萍閱讀 322評論 0 0
  • 三年前愚臀,你老是清晨去拉屎,壓力太大矾利,有時(shí)瘋有時(shí)崩姑裂。老是分享你們的愛情故事(虐狗),我們最瘋狂的事就是男旗,考試前一天逃...
    訴舊閱讀 302評論 0 0