青训营X豆包MarsCode 技术训练营刷题技巧(动态规划专题篇五)| 豆包MarsCode AI 刷题

88 阅读4分钟

找出最长的神奇数列

问题描述

image.png

问题分析

这道题一开始我是用贪心来解决,先说下贪心和动规的区别,第一篇有提到是局部最优和全局最优,这是核心的。 这道题目标是找到一个符合条件的最长子序列(即长度最大且 01 交替的子串),如果我们将每个位置的状态和当前最长的“神奇数列”相关联,并通过递推式计算,则可以转化为动态规划问题。这篇帖子既然是动规专题,这里我的解法会使用动态规划,两个解法的代码都会给出。 本题可以归类为最长子序列型动态规划问题的一种变体,与最长递增子序列(LIS)最长公共子序列(LCS) 问题类似。不同的是,本题的状态和转移逻辑与 字符串交替模式 相关,而非数值的单调性或相似性。

与其他动态规划问题的区别
(1)与**最长递增子序列(LIS)**的区别
  • LIS

    • 目标是找到最长的子序列,使得子序列单调递增。
    • 每个状态 dp[i] 表示以第 i 个元素结尾的最长递增子序列。
    • 状态转移方程:若 nums[j] < nums[i],则 dp[i] = max(dp[i], dp[j] + 1)
  • 本题

    • 目标是找到最长子串,使得子串交替由 01 组成。
    • 子串必须是连续的,因此无法像 LIS 一样自由选择元素。
(2)与**最长公共子序列(LCS)**的区别
  • LCS

    • 目标是找到两个序列的最长公共子序列。
    • 状态定义:dp[i][j] 表示序列 Ai 个字符与序列 Bj 个字符的最长公共子序列长度。
    • 转移方程:若 A[i] == B[j],则 dp[i][j] = dp[i-1][j-1] + 1,否则 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  • 本题

    • 目标是从单一序列中找到满足交替规则的子串。
    • 不涉及两个序列之间的匹配,而是针对一个序列的特定模式。

动态规划状态分析

(1)状态定义
  • dp[i] 表示以位置 i 为结尾的最长“神奇数列”的长度。

    • 如果位置 i 的字符可以与前一个字符交替,则可以延续子串。
    • 否则,从当前字符重新开始计算。
(2)状态转移方程
  • inp[i] != inp[i-1](满足交替规则):

    • dp[i] = dp[i-1] + 1
  • 否则:

    • dp[i] = 1(从当前位置重新开始计算)
(3)边界条件
  • 初始状态:

    • dp[0] = 1,因为以第一个字符为结尾的子串长度为 1。
(4)目标
  • 遍历 dp 数组,找到最大值 max(dp[i]),表示最长的“神奇数列”的长度。

Python代码(贪心)

def solution(inp):
    max_length = 0
    max_start = 0

    for i in range(len(inp)):
        # 检查从 i 开始的子序列是否是神奇数列
        length = 1
        while i + length < len(inp) and inp[i + length] != inp[i + length - 1]:
            length += 1

        # 如果长度大于等于3,更新最长神奇数列的信息
        if length >= 3:
            if length > max_length:
                max_length = length
                max_start = i

    # 返回最长的神奇数列
    return inp[max_start:max_start + max_length]


if __name__ == "__main__":
    # Add your test cases here

    print(solution("0101011101") == "010101")

Python代码(DP)

def solution(inp):
    n = len(inp)
    if n < 3:
        return ""  # 长度不足3,不可能存在神奇数列

    max_length = 0
    max_start = 0

    i = 0
    while i < n:
        # 检查从 i 开始是否可以形成神奇数列
        length = 1
        while i + length < n and inp[i + length] != inp[i + length - 1]:
            length += 1

        # 更新最长神奇数列的信息
        if length >= 3 and length > max_length:
            max_length = length
            max_start = i

        # 移动到下一个可能的起点
        i += length  # 跳过当前子串

    # 如果没有符合条件的神奇数列,返回空字符串
    if max_length < 3:
        return ""

    # 返回最长的神奇数列
    return inp[max_start:max_start + max_length]


# 测试用例
print(solution("0101011101"))  # 输出 "010101"
print(solution("111010101000"))  # 输出 "101010"
print(solution("101010101010"))  # 输出 "101010101010"
print(solution("01111"))  # 输出 ""