思路
看以下题解,思路十分清晰。也很好地解释了递归、记忆化搜索递归以及DP之间的关系。
【PS:虽然我做的时候想到了要点就是,每个字符选择/不选择的判断,并尝试递归,哈哈但我不会写。】
这里的核心递归(dp递推)公式我用一个案例解释:
s:bagg,t:bag
- 当s[3] ==t[2]时,即末尾的g相等,此时我可以选择
- 保留末尾g的匹配,然后用剩下的s[0-2] 匹配t[0-1],s:bagg,t:bag
- 虽然末尾g相等,但s不care这个相等,用s[0-2] 匹配t[0-2],s:bagg,t:bag
- 总的结果是这两种情况相加。
当s[3] !==t[2]时,即末尾的g不相等,此时只能选择
用s[0-2]去匹配完整的t,即t[0-2],用bag匹配bag,s:bagg,t:bag
递归(超时)
var numDistinct = function(s, t) {let sLen =s.length,tLen =t.lengthconst dfs =function(i,j){//从大往小递归,结束在i和j小于0时if(j<0){//t为空串,空串可以被任意匹配return 1}if(i<0){//s为空,无法匹配任何字符串return 0}if(s[i]===t[j]){return dfs(i-1,j-1)+dfs(i-1,j)}else{return dfs(i-1,j)}}return dfs(sLen-1,tLen-1)};
记忆化搜索递归
var numDistinct = function(s, t) { let sLen =s.length,tLen =t.length // 记忆化搜索数组 let memo =new Array(sLen).fill(-1).map(()=>new Array(tLen).fill(-1)) const dfs =function(i,j){ //从大往小递归,结束在i和j小于0时 if(j<0){ //t为空串,空串可以被任意匹配 return 1 } if(i<0){ //s为空,无法匹配任何字符串 return 0 } if(memo[i][j]!==-1) return memo[i][j] if(s[i]===t[j]){ memo[i][j] =dfs(i-1,j-1)+dfs(i-1,j) return memo[i][j] }else{ memo[i][j] =dfs(i-1,j) return memo[i][j] } } return dfs(sLen-1,tLen-1) };DP
注意,还是按照惯例来做吧,免得混乱。
也就是 dp[i][j] 代表的是dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。 ```javascript var numDistinct = function(s, t) { let sLen =s.length,tLen =t.length // dp let dp =new Array(sLen+1).fill().map(()=>new Array(tLen+1).fill(0)) for(let i=0;i<=sLen;i++){ dp[i][0] =1 } for(let i=1;i<=sLen;i++){ for(let j=1;j<=tLen;j++){if(s[i-1]===t[j-1]){ dp[i][j] =dp[i-1][j-1]+dp[i-1][j] }else{ dp[i][j] =dp[i-1][j] }} }
return dp[sLen][tLen]
};
```

