返回

力扣:不同子序列——动态规划里的无底洞

闲谈

力扣:动态规划之不同的子序列

又到了我们的动态规划时间,今天让你们再次感受一下动态规划的恐怖之处。我也是刚刚从里面走出来,就立马迫不及待地和你们分享,我可不想让自己白白掉这么多头发,应该让各位一起来掉掉头发,才能维持我内心的平衡。

当打开力扣的动态规划题库,映入眼帘的一道题名为不同子序列的题目,其题目标题就让人毛骨悚然,涉及动态规划,我们就要面临一场烧脑大战,满篇的递推公式,往往让初学者眼花缭乱。

进入题目界面,题意是这样的:给定一个字符串和一个目标字符串,要求我们找出字符串中包含目标字符串的所有子序列。子序列不同于子串,子序列允许不连续的字符。

例如,字符串“abc”的子序列包括“a”、“b”、“c”、“ab”、“ac”、“bc”和“abc”。

这道题的解题方法有很多,但其中最经典的方法之一便是动态规划。

动态规划是一种解决问题的策略,它将一个问题分解成若干个子问题,并逐一解决这些子问题,最后将这些子问题的解组合起来,得到整个问题的解。

对于这道题,我们可以将问题分解成若干个子问题,即找出字符串中包含目标字符串所有子序列的子问题。

具体而言,我们可以定义一个二维数组dp,其中dp[i][j]表示字符串s的前i个字符是否包含目标字符串t的前j个字符。

如果dp[i][j]为真,则表示字符串s的前i个字符包含目标字符串t的前j个字符。如果dp[i][j]为假,则表示字符串s的前i个字符不包含目标字符串t的前j个字符。

我们可以使用以下递推公式来计算dp数组:

dp[i][j] = dp[i-1][j-1] if s[i] == t[j]
dp[i][j] = dp[i-1][j] otherwise

其中,s[i]表示字符串s的第i个字符,t[j]表示目标字符串t的第j个字符。

这个递推公式的含义是:

如果字符串s的第i个字符与目标字符串t的第j个字符相等,那么字符串s的前i个字符包含目标字符串t的前j个字符,当且仅当字符串s的前i-1个字符包含目标字符串t的前j-1个字符。

如果字符串s的第i个字符与目标字符串t的第j个字符不相等,那么字符串s的前i个字符包含目标字符串t的前j个字符,当且仅当字符串s的前i-1个字符包含目标字符串t的前j个字符。

有了这个递推公式,我们就可以从左到右、从上到下地计算dp数组。当我们计算完dp数组之后,我们就可以根据dp数组来找出字符串s中包含目标字符串t的所有子序列。

具体而言,我们可以从字符串s的最后一个字符开始,向左回溯。如果dp[i][j]为真,则表示字符串s的前i个字符包含目标字符串t的前j个字符。此时,我们就可以将字符串s的第i个字符添加到子序列中。然后,我们继续向左回溯,直到找到一个dp[i][j]为真的位置,将字符串s的第i个字符添加到子序列中。如此往复,直到我们回溯到字符串s的第一个字符。

最后,我们将得到的子序列输出即可。

这道题的动态规划解法虽然比较复杂,但是它的思路却非常清晰。只要我们一步一步地按照递推公式计算dp数组,然后根据dp数组回溯即可。

希望这篇文章能够帮助大家理解动态规划的思想和方法,并能够在以后的算法竞赛中取得好成绩。