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;
}
};