1.題目描述
給定一個字符串 S 和一個字符串 T,計算在 S 的子序列中 T 出現的個數。
一個字符串的一個子序列是指,通過刪除一些(也可以不刪除)字符且不干擾剩餘字符相對位置所組成的新字符串。(例如,"ACE" 是 "ABCDE" 的一個子序列,而 "AEC" 不是)
示例 1:
輸入: S = "rabbbit", T = "rabbit"
輸出: 3
解釋:
如下圖所示, 有 3 種可以從 S 中得到 "rabbit" 的方案 (上箭頭符號 ^ 表示選取的字母)
示例 2:
如下圖所示, 有 5 種可以從 S 中得到 "bag" 的方案。
(上箭頭符號 ^ 表示選取的字母)
2. 解題思路
這是一道動態規劃題目,假設 dp[i][j] 表示字符串 T[0,i-1] 在字符串S[0,j-1]出現次數。
第一步找出狀態轉移方程:
求一般情況下 dp[i][j] 的值。
這裏分兩種情況:①字符串T的第 ( i - 1) 個字符等於字符串S的第( j - 1 )個字符。②兩個字符不相等
第二種比較容易推斷,不相等時,出現次數等於 dp[i][j-1] 次數(相當於刪除字符串S的第( j - 1 )字符)
第一種情況下,出現次數等於 dp[i-1][j-1] + dp[i][j-1] 。這裏得考慮兩種情況,當字符串S的第( j - 1 )字符不刪除時,出現次數跟
dp[i-1][j-1] 相同,刪除情況下,出現次數爲 dp[i][j-1],兩種情況相加可得 dp[i-1][j-1] + dp[i][j-1]。
第二步考慮邊界情況:
當字符串T長度爲 0 時, 出現次數dp[0][x]( 0 =<x <= S長度)皆爲 1(即每個子問題都刪除所有字符)
當字符串S長度爲 0 時, 字符串T的所有子串出現次數都爲0.
爲方便理解,以示例2輸入進行填表:
下標 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
對應字符 | 0 | b | a | b | g | b | a | g | |
0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | b | 0 | 1 | 2 | 2 | 2 | 3 | 3 | 3 |
2 | a | 0 | 0 | 1 | 1 | 1 | 1 | 4 | 4 |
3 | g | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 5 |
3.示例代碼
class Solution {
public int numDistinct(String s, String t) {
int[][] dp = new int[t.length()+1][s.length()+1];
// 邊界賦值
for(int i = 0;i < s.length()+1;i++){
dp[0][i] = 1;
}
for(int i = 1;i < t.length()+1;i++){
for(int j = 1;j < s.length()+1;j++){
if(s.charAt(j-1) == t.charAt(i-1)){
dp[i][j] = dp[i-1][j-1] + dp[i][j-1];
}else{
dp[i][j] = dp[i][j-1];
}
}
}
return dp[t.length()][s.length()];
}
}