【动态规划】不同子的序列

这篇博客详细介绍了如何运用动态规划解决力扣题目115:不同子序列的问题。通过建立二维数组dp[][],根据字符串s和t的长度进行遍历,确定状态转移方程,最终计算出t在s的子序列中出现的次数。文章包含解题步骤、状态定义、转移方程、初始条件和边界情况,并给出了计算顺序及时间复杂度分析。

问题

115.不同的子序列(力扣)

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

(原题链接)

动态规划解题步骤

1、概述

建立二位数组dp[][],行表示串s[0...m-1],列表示串t[0...n-1],串在数组中从下标为[1]开始。

例:s = "rabbbit", t = "rabbit",如图:其中s[0]对应列dp[][1],t[0]对应行dp[1][]

约定:串t在串s子序列中出现的个数记为num[s,t];dp[i][j]的值表示t的子串t'[0...i-1]在s的子串s'[0...j-1]子序列中出现的个数,即num[s',t']。

2、确定状态

(1)最后一步

如图:

此时s的子串s'[0...m-2]与t的子串t[0...n-1]已经比对完毕,num[s',t]已经求得,num[s',t']表示串t'在s'子序列中出现的个数。

对比字符s[m-1]与t[n-1],若二者不相等,则num[s,t]=num[s',t]+0,即没有新增;若二者相等,则num[s,t]=num[s',t]+num[s',t'],仔细思考一下,此时多出来的个数为串t'在串s'子序列中出现的个数,因为串t的最后一个字符多了一个位置,便会增多num[s',t']种情况。

(2)子问题

原问题是求串s[0...m-1]子序列中串t[0...n-1]出现的个数,即求num[s,t];

子问题是求子串s'[0...j-1]子序列中子串t'[0...i]出现的个数,即求num[s',t']。

3、转移方程

根据上述约定,dp[i][j-1]=num[s',t],dp[i-1][j-1]=num[s',t']。

故比对到的字符如果不相等:dp[i][j]=dp[i][j-1]

如果相等:dp[i][j]=dp[i-1][j-1]+dp[i][j-1]

4、初始条件和边界情况

初始条件:数组第一行dp[0][]的值均为1

边界情况:列号>=行号,因为若s'.length()<t'.length(),则s'中必定不存在子序列t';遍历到最后一个字符时结束

5、计算顺序

按行优先遍历,即先计算t'[0...i]在串s子序列中出现的个数,再计算t''[0...i+1]在串s子序列中出现的个数。

时间和空间复杂度均为O(m*n)

代码:

class Solution {
    public int numDistinct(String s, String t) {
        int m=s.length(),n=t.length();
        int[][] dp=new int[n+1][m+1];
        for (int j=0;j<m;++j) dp[0][j]=1;
        for (int i=1;i<=n;++i){
            for (int j=i;j<=m;++j){
                if(t.charAt(i-1)==s.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
                else dp[i][j]=dp[i][j-1];
            }
        }
        return dp[n][m];
    }
}

不足之处或优化建议希望能在评论区分享,一起学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值