构造回文字符串刷题心得 | 豆包MarsCode AI 刷题

126 阅读4分钟

分析和思路

  1. 回文性质

    • 回文字符串 ttt 的对称性约束意味着:

      • 如果 t[i]t[i]t[i] 确定了,那么 t[n−i−1]t[n-i-1]t[n−i−1] 也必须是相同的字符(其中 n 是 t 的长度)。
  2. 字典序要求

    • t<st < st<s,表示 ttt 的字典序必须严格小于 s。
    • 需要从左向右逐字符构造 t,在可能情况下选择比对应的 s[i]s[i]s[i] 小的字符,并使得 t 满足回文的同时字典序尽可能接近 s。
  3. 构造原则

    • 遍历字符串 s 的前半部分(因为回文的对称性决定了后半部分)。
    • 尝试找到第一个可以减小的字符(使 t[i]<s[i]t[i] < s[i]t[i]<s[i]),然后在后续补充满足回文的字符。
  4. 无法构造的情况

    • 如果 s 本身是最小的回文,或者无法通过减少字典序的方式保持 t 为回文并小于 s,则输出 -1

实现步骤

输入:

一个字符串 s。

输出:

一个符合条件的字符串 t,或者 -1

算法实现

`def construct_palindrome(s): n = len(s) s_list = list(s)

# Step 1: 初始化 t 和对称性的限制
t = list(s)

# Step 2: 遍历 t 的前半部分
for i in range((n + 1) // 2):
    # 当前字符 t[i] 需要比 s[i] 小,同时保持对称性
    for c in range(ord(s[i]) - 1, ord('a') - 1, -1):
        t[i] = chr(c)
        t[n - i - 1] = chr(c)  # 对称填充
        
        # 如果 t 已经构成回文并小于 s,则返回 t
        if "".join(t) < s:
            return "".join(t)
    
    # 若无法找到有效的 t[i],还原 t[i]
    t[i] = s[i]
    t[n - i - 1] = s[n - i - 1]

# Step 3: 如果没有找到合适的 t,返回 -1
return "-1"

测试

s = "abcdcba" print(construct_palindrome(s)) # 示例输出 `

算法解释

  1. 遍历 s 的前半部分,因为后半部分由回文的性质自动填充。
  2. 尝试在回文对称的基础上,从左到右寻找比当前字符小的字符,并验证是否能构造有效的 t。
  3. 如果没有找到符合条件的 t,返回 -1

复杂度分析

  • 时间复杂度:O(n),因为我们只需线性扫描字符串的一半长度。
  • 空间复杂度:O(n),用于存储临时的字符串 t。

心得总结

  1. 问题拆解的重要性

    • 这个问题表面上看起来很复杂,因为同时涉及回文性质、字典序比较和构造条件。但将问题分解成:

      1. 保持回文对称性;
      2. 确保字典序尽可能接近;
      3. 从左到右逐步构造; 后,复杂度和实现难度都大幅降低。
  2. 对回文性质的理解

    • 回文性质是解题的核心,因为它让我们只需要关注字符串的一半,大大减少了需要处理的字符数。这种"对称性"约束是许多问题的关键优化点。
  3. 贪心策略的使用

    • 通过从左到右扫描,找到第一个可减小字典序的位置,然后立即尝试构造满足条件的结果。这种“尽早做出决策”的贪心策略非常高效,同时还能保证构造的字符串在字典序上尽可能大。
  4. 边界情况处理

    • 边界情况(如无法构造出满足条件的回文)是问题的难点之一。通过按字符逐步尝试,并在尝试失败后返回 -1,能够确保程序的健壮性。
  5. 代码的调试与测试

    • 编写测试用例帮助发现潜在问题:

      • 特殊输入如全为相同字符("aaaaa")。
      • 奇偶长度不同的字符串。
      • 无法找到满足条件的情形。
    • 测试用例覆盖不同情况,可以确保代码的正确性。

  6. 时间与空间效率的平衡

    • 该算法的时间复杂度为 O(n)O(n)O(n),空间复杂度也控制在 O(n)O(n)O(n),已经是该问题的最优复杂度。这表明在解决问题时,我们既关注功能,也关注性能。