探索最长「神奇数列」问题解析 | 豆包MarsCode AI刷题

75 阅读6分钟

学习记录与心得:探索最长「神奇数列」问题

在本次学习中,我接触并解决了一个有趣的数列问题,主题是从一个由 01 组成的正整数序列中找到最长的「神奇数列」。通过分析问题、设计算法并优化代码,我不仅加深了对字符串处理的理解,还总结了宝贵的编程经验。以下是我的学习过程与心得记录。


问题描述分析

问题的核心是寻找最长的「神奇数列」。
「神奇数列」的定义如下:

  1. 数列中的 01 必须严格交替出现;
  2. 数列长度至少为 3。

例如:

  • 序列 10101 是「神奇数列」,因为它是交替的且长度大于等于 3;
  • 序列 1011 不是,因为末尾没有完全交替。

需要解决的问题是:

  1. 从输入的序列中找到最长的「神奇数列」;
  2. 如果有多个相同长度的序列,返回第一个出现的。

思路解析

1. 观察规律

一个序列是「神奇数列」的关键在于:

  • 每个数字必须与前一个数字不同;
  • 该交替序列的长度必须达到 3。

例如:

  • 对于输入 0101011101

    • 子序列 010101 满足交替性,且长度为 6;
    • 子序列 101 也满足交替性,但长度较短;
    • 返回结果应为 010101

2. 分步解决思路

方法一:暴力枚举

  • 枚举所有可能的子序列:用两个指针分别确定子序列的起点和终点;
  • 验证交替性:对子序列逐一判断是否满足「神奇数列」的定义;
  • 记录最长的序列:更新长度和起始位置。

优点:简单易懂,适合小规模问题。
缺点:复杂度较高,时间复杂度 O(n3)O(n^3)


方法二:滑动窗口优化

  • 滑动窗口是一种减少冗余计算的技巧:

    1. 窗口定义:找到交替开始的位置,记录窗口长度;
    2. 动态扩展窗口:只要交替性成立,窗口右边界继续扩展;
    3. 窗口收缩:一旦交替性中断,移动左边界重置窗口;
    4. 更新结果:在滑动过程中,记录最长的「神奇数列」。

优点:减少重复判断,时间复杂度降至 O(n)O(n)


3. 图解滑动窗口法

假设输入为:0101011101

  • 初始时,窗口的起点为索引 0,窗口内元素为 0101
  • 遇到 11 时,交替性中断,窗口起点移动到 7
  • 继续向后扩展,窗口元素变为 101

通过动态调整窗口,可以快速找到最长的交替子序列。


代码详解

方法一:暴力解法

暴力解法通过枚举所有子序列,判断其是否满足交替性:

# 判断子序列是否是交替的 0 和 1
def is_alternating(subseq):
    for k in range(1, len(subseq)):
        if subseq[k] == subseq[k - 1]:
            return False
    return True

def solution_bruteforce(inp):
    max_length = 0
    max_start = 0
    
    # 遍历序列,枚举所有可能的子序列
    for i in range(len(inp)):
        for j in range(i + 3, len(inp) + 1):  # 子序列至少长度为 3
            subseq = inp[i:j]
            if is_alternating(subseq):
                # 如果当前子序列更长,更新记录
                if len(subseq) > max_length:
                    max_length = len(subseq)
                    max_start = i
    
    # 返回最长的「神奇数列」
    return inp[max_start:max_start + max_length]

# 测试
if __name__ == "__main__":
    assert solution_bruteforce("0101011101") == "010101"
    assert solution_bruteforce("1110101010000") == "10101010"
    assert solution_bruteforce("1010101010101010") == "1010101010101010"

代码解释:

  1. 外层循环 i 遍历序列起点;
  2. 内层循环 j 遍历序列终点;
  3. is_alternating 检查当前子序列的交替性;
  4. 如果子序列满足条件且更长,则更新记录。

方法二:滑动窗口优化

滑动窗口通过一次遍历动态调整窗口范围,实现效率提升:

def solution_optimized(inp):
    max_length = 0
    max_start = 0
    start = 0  # 当前窗口起始位置
    
    for i in range(1, len(inp)):
        # 如果交替性中断
        if inp[i] == inp[i - 1]:
            start = i  # 更新窗口起始位置
        else:
            # 当前窗口长度
            length = i - start + 1
            if length > max_length:
                max_length = length
                max_start = start
    
    # 如果长度不足 3,返回空
    return inp[max_start:max_start + max_length] if max_length >= 3 else ""

# 测试
if __name__ == "__main__":
    assert solution_optimized("0101011101") == "010101"
    assert solution_optimized("1110101010000") == "10101010"
    assert solution_optimized("1010101010101010") == "1010101010101010"

代码解释:

  1. 使用 start 记录当前窗口的起点;
  2. 遇到交替性中断(如连续两个 0),重置 start
  3. 在每次扩展窗口时,动态计算长度并更新结果;
  4. 最后返回记录的最长子序列。

算法效率对比

方法时间复杂度空间复杂度优点缺点
暴力解法O(n3)O(n^3)O(1)O(1)简单易实现性能较差
滑动窗口优化O(n)O(n)O(1)O(1)高效,适合大规模输入实现稍复杂

测试与验证

测试用例

输入输出解释
"0101011101""010101"最长序列为 "010101"
"1110101010000""10101010"最长交替序列为 "10101010"
"1010101010101010""1010101010101010"整个输入本身是最长序列
"000111000"""没有满足条件的子序列
"101""101"短序列直接返回本身

运行结果

两种方法在上述测试用例中均表现正确,优化方法明显更快。


学习心得

解决这个问题主要考虑的几个关键点:

  1. 算法设计能力:通过两种不同思路解决问题,暴力方法更容易理解,滑动窗口体现算法优化的重要性。
  2. 边界条件处理:在设计代码时注意考虑特殊情况,如序列过短或无有效子序列。
  3. 实践编程技巧:滑动窗口是一种高效处理字符串和数组问题的通用技巧,可以广泛应用于类似问题。
  4. 分而治之 将复杂问题拆解为多个子问题,例如:子序列生成、交替性判断、结果记录等,每部分独立实现再组合,代码更具可读性。

这次学习让我不仅掌握了字符串处理的技巧,还巩固了算法设计的思维逻辑,为今后的编程实践奠定了基础。