1. 程式人生 > >LeetCode—動態規劃

LeetCode—動態規劃

T5 最長迴文子串

給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為1000。

示例 1:
輸入: “babad”
輸出: “bab”
注意: “aba”也是一個有效答案。

示例 2:
輸入: “cbbd”
輸出: “bb”

傳統的驗證迴文串的方法就是兩個兩個的對稱驗證是否相等,那麼對於找回文字串的問題,就要以每一個字元為中心,像兩邊擴散來尋找回文串,這個演算法的時間複雜度是O(n*n)

要注意奇偶情況,由於迴文串的長度可奇可偶,比如”bob”是奇數形式的迴文,”noon”就是偶數形式的迴文,兩種形式的迴文都要搜尋

動態規劃

我們維護一個二維陣列dp,其中dp[i][j]表示字串區間[i, j]是否為迴文串,
(1)當i = j時,只有一個字元,肯定是迴文串,
(2)如果i = j + 1,說明是相鄰字元,此時需要判斷s[i]是否等於s[j],
(3)如果i和j不相鄰,即i - j >= 2時,除了判斷s[i]和s[j]相等之外,
dp[j + 1][i - 1]若為真,就是迴文串,通過以上分析,可以寫出遞推式如下:

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

這裡有個有趣的現象就是如果我把下面的程式碼中的二維陣列由int改為
vector< vector < int> >後,就會超時,這說明int型的二維陣列訪問執行速度完爆std的vector啊,所以以後儘可能的還是用最原始的資料型別吧。

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        int dp[n][n] = {0};
        int left=0, right=0, len=0;

        for(int i=0; i<n; i++)
        {
            dp[i][i] = 1;   // 一個字元肯定是迴文

            for(int j=0; j<i; j++)
            {
                dp[j][i] = {s[j]==s[i] && (i-j<2
|| dp[j+1][i-1])}; // 動態規劃,前一個字串也是迴文串 if(dp[j][i] && len<i-j+1) // 之前回文串短於當前迴文串 { len = i-j+1; left = j; right = i; } } } return s.substr(left, right-left+1); } };

T96 不同的二叉搜尋樹

給定一個整數 n,求以 1 … n 為節點組成的二叉搜尋樹有多少種?

這道題實際上是卡塔蘭數的不像斐波那契數那樣人盡皆知

我們先來看當 n = 1的情況,只能形成唯一的一棵二叉搜尋樹,n分別為1,2,3的情況如下所示:
這裡寫圖片描述

這裡寫圖片描述

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;
        dp[1]=1;

        for(int i=2; i<=n; i++)
        {
            for(int j=0; j<i; j++)
                dp[i] = dp[i] + dp[j]*dp[i-j-1];
        }
        return dp[n];
    }
};

T392 判斷子序列

給定字串 s 和 t ,判斷 s 是否為 t 的子序列。

示例 1:
s = “abc”, t = “ahbgdc”
返回 true.

示例 2:
s = “axc”, t = “ahbgdc”
返回 false.

這道題算比較簡單的一種,我們可以用兩個指標分別指向字串s和t,然後如果字元相等,則i和j自增1,反之只有j自增1,最後看i是否等於s的長度,等於說明s已經遍歷完了,而且字元都有在t中出現過

class Solution {
public:
    bool isSubsequence(string s, string t) {
        if(s.empty()) return true;
        int i=0,j=0;

        while(i<s.size() && j<t.size())
        {
            if(s[i]==t[j])
            {
                ++i;
                ++j;
            }
            else
                ++j;
        }
        return i==s.size();
    }
};

T647 迴文字串

給定一個字串,你的任務是計算這個字串中有多少個迴文子串。

具有不同開始位置或結束位置的子串,即使是由相同的字元組成,也會被計為是不同的子串。

示例 1:
輸入: “abc”
輸出: 3
解釋: 三個迴文子串: “a”, “b”, “c”.

示例 2:
輸入: “aaa”
輸出: 6
說明: 6個迴文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”.

需要一個二維陣列來記錄子字串[i, j]是否是迴文串,那麼我們直接就將dp[i][j]定義成子字串[i, j]是否是迴文串就行了,然後我們i從n-1往0遍歷,j從i往n-1遍歷,然後我們看s[i]和s[j]是否相等,這時候我們需要留意一下,有了s[i]和s[j]相等這個條件後,i和j的位置關係很重要,如果i和j相等了,那麼dp[i][j]肯定是true;如果i和j是相鄰的,那麼dp[i][j]也是true;如果i和j中間只有一個字元,那麼dp[i][j]還是true;如果中間有多餘一個字元存在,那麼我們需要看dp[i+1][j-1]是否為true,若為true,那麼dp[i][j]就是true。賦值dp[i][j]後,如果其為true,結果res自增1,參見程式碼如下:

class Solution {
public:
    int countSubstrings(string s) {
        int n=s.size();
        int res=0;
        vector<vector<bool>> dp(n,vector<bool>(n,false));

        for(int i=n-1; i>=0; i--)
        {
            for(int j=i; j<n; j++)
            {
                dp[i][j] = (s[i]==s[j]) && (j-i<=2 || dp[i+1][j-1]);
                if(dp[i][j]) 
                    ++res;
            }
        }
        return res;
    }
};