动态规划—不同子序列 解析+代码

不同子序列

题目链接:不同的子序列

题目描述:

在这里插入图片描述

因为需要前字符匹配的状态,所以我们考虑可以使用动态规划

状态是一维数组还是二维数组?

使用一维数组还是二维数组视情况而定。
题目所求是字符串S有几个子序列和T匹配,可能有人就认为只需要记录分解S就好了,但是如果只用S的子序列去匹配T,那么就要求子序列的长度必须大于等于T的长度,这是无法规划,无法定义出状态的。
所以我们尝试将用S的子序列去匹配T的子序列
状态定义为:S的 i 个字符匹配T的 j 个字符
所以我们使用二维数组 F(i ,j)

状态转移

状态:S的前 i 个字符和T的前 j 个字符的匹配个数
要匹配成功,S的第 i 个字符和T的第 j 个字符就必须一样
当S[ i ] == T[ j ]时,那么只要S的前 i-1 个字符和T的前 j-1 个字符也可以匹配就好
但是也存在像 S = “rabbb”,T = “rabb”,虽然S[ 5 ] ==T[ 4 ],但是使用S的最后一个字符一样可以匹配成功
所以 S[ i ] ==T[ j ]有两种情况:使用S的最后一个字符 or 不使用
因为是求次数,所以两种情况相加

当S [ i ] != T [ j ]时,就缩小问题,看S的前 i-1 个字符和T的前 j-1 个字符有多少匹配

所以状态转移方程:
S[ i ] ==T[ j ],F(i,j) = F(i-1,j-1) + F(i-1,j)
S[ i ] !=T[ j ], F(i,j) = F(i-1,j)

初始状态

若 i=0时,代表S的空串去匹配T的子串,不管T是什么,都是可以匹配成功的,所以F(i,0)=1
而 j=0时,代表S的子串去匹配T的空串,则无法匹配,所以F(0,j)=0

代码

int numDistinct(string S, string T)
    {
        // write code here
        int row=S.size();
        int col=T.size();

        vector<vector<int>>dp(row+1,vector<int>(col+1,0));

        dp[0][0]=1;
        for(int i=1;i<=row;++i)
        {
            dp[i][0]=1;
            for(int j=1;j<=col;++j)
            {
                //最后一个字符相同
                //dp[i-1][j-1]是使用匹配的最后一个字符
                //dp[i-1][j]是不使用匹配的最后一个字符
                if(S[i-1]==T[j-1])
                    dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                else//不相同,转化成子问题S的i-1个字符匹配T的j个字符
                    dp[i][j]=dp[i-1][j];
            }
        }

        return dp[row][col];
    }

空间优化

但是在实际使用中,我们发现二维数组的数据其实每次只用到当前行的上一行,所以我们可以将其优化成一维数组,但是 j 循环的起始应是最右边,从右往左更新,才不会提前覆盖所需数据

代码如下:

//一维数组
    int numDistinct(string S, string T)
    {
        // write code here
        int row=S.size();
        int col=T.size();

        vector<int>dp(col+1,0);

        dp[0]=1;
        for(int i=1;i<=row;++i)
        {
        	//从右往左更新
            for(int j=col;j>=0;--j)
            {
                if(S[i-1]==T[j-1])
                    dp[j]=dp[j-1]+dp[j];
                 //else删除行后变成 dp[j]=dp[j],所以省略
            }
        }

        return dp[col];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值