刷题记录 | 动态规划算法题 - 子序列和子串相关

最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

注意区分:子序列和子串的区别。子序列的话只要元素都存在,并且顺序一致就可以。

动态规划的思路:

第一步:明确 dp 数组的含义。

对于字符串 str1str2,一般都需要构造这样的一个 table。

1
dp[i][j] 的含义是:对于 str1[1..i]str2[1...j], 他们的 LCS 长度是 dp[i][j].

第二步:定义 base case。

我们专门让索引为 0 的行和列表示空串,dp[0][…] 和 dp[…][0] 都应该初始化为 0,这就是 base case。

比如说,按照刚才 dp 数组的定义,dp[0][3]=0 的含义是:对于字符串 “” 和 “bab”,其 LCS 的长度为 0。因为有一个字符串是空串,它们的最长公共子序列的长度显然应该是 0。

第三步:定义状态转移方程

如果某个字符应该在 lcs 中,那么这个字符肯定同时存在于 s1 和 s2 中,因为 lcs 是最长公共子序列嘛。所以本题的思路是这样:

用两个指针 i 和 j 从后往前遍历 s1 和 s2,如果 s1[i]==s2[j],那么这个字符一定在 lcs 中;否则的话,s1[i] 和 s2[j] 这两个字符至少有一个不在 lcs 中,需要丢弃一个。

Python 代码是这样的:

def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        # 动态规划的解法
        m, n = len(text1), len(text2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if text1[i-1] == text2[j-1]:  # dp 第一行和第一列是空下的0情况
                    # 找到一个lcs中的字符
                    dp[i][j] = 1 + dp[i-1][j-1]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
        return dp[-1][-1]

最长回文子序列

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。

示例 1:

输入:
"bbbab"

输出:
4

一个可能的最长回文子序列为 “bbbb”。

用动态规划来做。

数组 dp[i][j] 表示 s[i:j] 最长回文子序列的长度。

对角线位置的动态数组的值为 1.

11

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0]*n for _ in range(n)]
        for j in range(n-1,-1,-1):
            dp[j][j] = 1
            for i in range(j+1,n):
                if s[j] == s[i]:
                    dp[j][i] = 2 + dp[j+1][i-1]
                else:
                    dp[j][i] = max(dp[j+1][i],dp[j][i-1])
        return dp[0][n-1]

统计全为1的正方形子矩阵

示例:

输入:matrix =
[
  [0,1,1,1],
  [1,1,1,1],
  [0,1,1,1]
]
输出:15
解释: 
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.

动态规划

def countSquares(self, matrix: List[List[int]]) -> int:
        m, n = len(matrix), len(matrix[0])
        f = [[0] * n for _ in range(m)]
        ans = 0
        for i in range(m):
            for j in range(n):
                if i == 0 or j == 0:  # 边界条件的判断。
                    f[i][j] = matrix[i][j]
                elif matrix[i][j] == 0:
                    f[i][j] = 0
                else:
                    f[i][j] = min(f[i][j-1], f[i-1][j], f[i-1][j-1]) + 1 
                    # 要注意的是 这个1 是加在外面的。代表着只要另外三个角的元素都是1,则就可以组成一个大正方形。
                ans += f[i][j]
        return ans

回文子串

判断给定字符串,有多少个回文子串。其中一个字母也算是回文子串。

解题思路:采用动态规划的方法。维护一个动态数组,代表s[i:j]是不是回文子串。

代码如下:

def countSubstrings(self, s: str) -> int:
        # dp[i][j] 代表子串[i,j]是否是一个子串
        n = len(s)
        dp = [[False] * n for _ in range(n)]
        count = 0
        # 枚举所有可能,因为代表子串,所以 i<=j
        for j in range(n):
            for i in range(0, j+1):
                length = j-i + 1
                if length == 1:
                    dp[i][j] = True
                    count += 1
                if length == 2 and s[i] == s[j]:
                    dp[i][j] = True
                    count += 1
                if length > 2 and s[i] == s[j] and dp[i+1][j-1] is True:
                    dp[i][j] = True
                    count += 1
        return count

猜你喜欢

转载自blog.csdn.net/weixin_38746310/article/details/108453871
今日推荐