携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
不同的子序列
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。 字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是) 来源:leetcode.cn/problems/di…
分析
- 求在s的子序列中t出现的次数,即s中匹配t出现的次数。尝试使用动态规划来解决这个题目。
动态规划
- 如果s至少匹配一次t,那s.length >= t.length,如果s.length < t.length,个数一定是0。
- 所以,s是比较长的字符串,t是稍短一些的字符串,所以在关注匹配次数的时候,默认t是固定的,s是递增的
- 状态定义:dp[i][j]表示s[0,i-1]字符串中能匹配到的t[0,j-1]的个数
- 状态转移说明:
S = "rabbbit", T = "rabbit"
T: rabbit-rabbit-rabbit-rabbit-rabbit-rabbit
S: r------ra-----rab----rabb---rabbb--rabbbit
- 当s[i-1]!==t[j-1] 时,不增加不同子序列的数量,而是要继续遍历s,所以可以得到dp[i][j] = dp[i-1][j]
-当s[i-1]===t[j-1],有两种情况:
- 1.不匹配s[i-1]和t[j-1],这时子序列数量是不变的,可以得出dp[i][j] = dp[i-1][j]
- 2.匹配s[i-1]和t[j-1]这两个字符,因此可以得出dp[i][j] = dp[i-1][j-1]
- 包括以上所有情况的状态转移公式是dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
代码
/**
* @param {string} s
* @param {string} t
* @return {number}
*/
var numDistinct = function(s, t) {
let n = s.length
let m = t.length
let dp = new Array(n+1)
for(let i=0;i<n+1;i++){
dp[i] = new Array(m+1)
dp[i][0]=1
}
for(let j=0;j<m+1;j++){
dp[0][j]=0
}
for(int j = 1;j<m+1;j++){
for(let i=1;i<n+1;i++){
dp[i][j] = dp[i-1][j]
if(s[i-1] === t[j-1]){
dp[i][j] += dp[i-1][j-1]
}
}
}
return dp[n][m]
};
总结
- 在解这种动态规划的题目的时候,比较困难的是怎么实现状态转移,在分析的时候为了防止状态太多,可以通过固定1个,另一个变化来看状态的转移,转移方程找到之后就能比较顺畅的写出来解法
- 今天也是有收获的一天