算法学习|线性dp

线性动态规划(Linear DP)

处理序列或线性结构上的最优化问题,状态转移沿单一方向进行

最长递增子序列(LIS)

问题:找到数组中最长的严格递增子序列长度。
状态:dp[i] 表示以 nums[i] 结尾的 LIS 长度。
转移方程:dp[i] = max(dp[j] + 1) 对所有 j < i 且 nums[j] < nums[i]

def length_of_lis(nums):
    n = len(nums)
    dp = [1] * n
    for i in range(n):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp) if n > 0 else 0

nums = [10, 9, 2, 5, 3, 7, 101, 18]
print(length_of_lis(nums))
# 输出 4(子序列 [2,5,7,101])

最长公共子序列(LCS)

问题:给定两个字符串 text1 和 text2,返回它们的最长公共子序列长度。
状态定义:dp[i][j] 表示 text1[0:i-1] 和 text2[0:j-1] 的 LCS 长度。
转移方程:
若 text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
否则:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])

def longest_common_subsequence(text1, text2):
    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[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

text1 = "abcde"
text2 = "ace"
print(longest_common_subsequence(text1, text2))
# 输出 3("ace")

最大子数组和(Kadane算法)

问题:找到数组中连续子数组的最大和。
状态定义:dp[i] 表示以 nums[i] 结尾的最大子数组和。
转移方程:
dp[i] = max(nums[i], dp[i-1] + nums[i])

def max_subarray(nums):
    max_sum = current_sum = nums[0]
    for num in nums[1:]:
        current_sum = max(num, current_sum + num)
        max_sum = max(max_sum, current_sum)
    return max_sum

nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
print(max_subarray(nums))
# 输出 6(子数组 [4,-1,2,1])

编辑距离

问题:将字符串 word1 转换为 word2 所需的最小操作数(插入、删除、替换)。
状态定义:dp[i][j] 表示 word1[0:i-1] 到 word2[0:j-1] 的最小操作数。
转移方程:
若 word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
否则:
dp[i][j] = 1 + min(
dp[i-1][j], # 删除 word1[i-1]
dp[i][j-1], # 插入 word2[j-1]
dp[i-1][j-1] # 替换
)

def min_edit_distance(word1, word2):
    m, n = len(word1), len(word2)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0] = i  # 删除所有字符
    for j in range(n+1): dp[0][j] = j  # 插入所有字符
    for i in range(1, m+1):
        for j in range(1, n+1):
            if word1[i-1] == word2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
    return dp[m][n]

print(min_edit_distance("horse", "ros"))  
#  3 可能?(horse→rorse→rose→ros)
posted @ 2025-04-08 15:38  lumiere_cloud  阅读(81)  评论(0)    收藏  举报