区间动态规划例题:最小插入次数构造回文串问题(相邻字符交换允许)
字数 1175 2025-11-19 12:13:10

区间动态规划例题:最小插入次数构造回文串问题(相邻字符交换允许)

题目描述
给定一个字符串s,你可以进行两种操作:

  1. 在任意位置插入一个字符(成本为1)
  2. 交换相邻的两个字符(成本为0)

问最少需要多少次插入(交换操作免费),才能将字符串变成回文串。

解题思路
这个问题是经典的回文串构造问题的变种,由于允许免费交换相邻字符,我们可以重新排列字符串中的字符。关键思路是:

  • 免费交换意味着我们可以以任意顺序排列字符
  • 问题转化为:通过最少插入使字符串能排列成回文串
  • 回文串的条件:最多一个字符出现奇数次,其他都出现偶数次

详细解题步骤

步骤1:问题分析
由于交换免费,我们只需关心每个字符的出现次数,而不关心它们的相对位置。
对于一个回文串:

  • 如果字符串长度是偶数:所有字符都必须出现偶数次
  • 如果字符串长度是奇数:恰好一个字符出现奇数次,其他都出现偶数次

步骤2:统计字符频率
首先统计字符串中每个字符的出现频率:

from collections import Counter
def min_insertions(s):
    freq = Counter(s)

步骤3:计算需要调整的字符
我们需要确保最多只有一个字符出现奇数次。
对于出现奇数次的字符,我们只能保留其中一个(如果字符串长度是奇数),其他的都需要调整。

计算出现奇数次的字符数量:

odd_count = sum(1 for count in freq.values() if count % 2 == 1)

步骤4:确定最小插入次数
如果odd_count <= 1,那么字符串已经可以通过重新排列变成回文串,不需要插入,答案为0。

如果odd_count > 1,我们需要通过插入操作来减少奇数个数的字符。
每个插入操作可以:

  • 要么将一个出现奇数次的字符变成出现偶数次
  • 要么减少奇数个数的字符数量

实际上,最小插入次数是max(0, odd_count - 1)

return max(0, odd_count - 1)

步骤5:完整算法实现

from collections import Counter

def min_insertions_to_palindrome(s):
    """
    计算将字符串通过插入和免费交换变成回文串的最小插入次数
    
    参数:
        s: 输入字符串
        
    返回:
        最小插入次数
    """
    # 统计字符频率
    freq = Counter(s)
    
    # 计算出现奇数次的字符个数
    odd_count = 0
    for count in freq.values():
        if count % 2 == 1:
            odd_count += 1
    
    # 最小插入次数为 max(0, 出现奇数次的字符数 - 1)
    return max(0, odd_count - 1)

步骤6:示例验证

例1:s = "aabb"

  • 频率:{'a':2, 'b':2},都是偶数
  • odd_count = 0
  • 结果:0(已经是回文"abba")

例2:s = "abc"

  • 频率:{'a':1, 'b':1, 'c':1},都是奇数
  • odd_count = 3
  • 结果:2(可以插入2个字符,如"abcba")

例3:s = "aab"

  • 频率:{'a':2, 'b':1},一个奇数
  • odd_count = 1
  • 结果:0(可以重排为"aba")

步骤7:算法正确性证明

  • 如果odd_count ≤ 1,字符串已经满足回文条件
  • 如果odd_count > 1,我们需要将多余的奇数个字符通过插入操作配对
  • 每个插入操作可以减少一个奇数个字符
  • 最终只需要保留一个奇数个字符(如果总长度为奇数)或0个(如果总长度为偶数)

步骤8:时间复杂度分析

  • 统计频率:O(n),其中n是字符串长度
  • 计算奇数个数:O(k),其中k是字符集大小
  • 总时间复杂度:O(n),非常高效

这个解法利用了免费交换的特性,将复杂的位置调整问题简化为简单的频率统计问题。

区间动态规划例题:最小插入次数构造回文串问题(相邻字符交换允许) 题目描述 给定一个字符串s,你可以进行两种操作: 在任意位置插入一个字符(成本为1) 交换相邻的两个字符(成本为0) 问最少需要多少次插入(交换操作免费),才能将字符串变成回文串。 解题思路 这个问题是经典的回文串构造问题的变种,由于允许免费交换相邻字符,我们可以重新排列字符串中的字符。关键思路是: 免费交换意味着我们可以以任意顺序排列字符 问题转化为:通过最少插入使字符串能排列成回文串 回文串的条件:最多一个字符出现奇数次,其他都出现偶数次 详细解题步骤 步骤1:问题分析 由于交换免费,我们只需关心每个字符的出现次数,而不关心它们的相对位置。 对于一个回文串: 如果字符串长度是偶数:所有字符都必须出现偶数次 如果字符串长度是奇数:恰好一个字符出现奇数次,其他都出现偶数次 步骤2:统计字符频率 首先统计字符串中每个字符的出现频率: 步骤3:计算需要调整的字符 我们需要确保最多只有一个字符出现奇数次。 对于出现奇数次的字符,我们只能保留其中一个(如果字符串长度是奇数),其他的都需要调整。 计算出现奇数次的字符数量: 步骤4:确定最小插入次数 如果 odd_count <= 1 ,那么字符串已经可以通过重新排列变成回文串,不需要插入,答案为0。 如果 odd_count > 1 ,我们需要通过插入操作来减少奇数个数的字符。 每个插入操作可以: 要么将一个出现奇数次的字符变成出现偶数次 要么减少奇数个数的字符数量 实际上,最小插入次数是 max(0, odd_count - 1) : 步骤5:完整算法实现 步骤6:示例验证 例1:s = "aabb" 频率:{'a':2, 'b':2},都是偶数 odd_ count = 0 结果:0(已经是回文"abba") 例2:s = "abc" 频率:{'a':1, 'b':1, 'c':1},都是奇数 odd_ count = 3 结果:2(可以插入2个字符,如"abcba") 例3:s = "aab" 频率:{'a':2, 'b':1},一个奇数 odd_ count = 1 结果:0(可以重排为"aba") 步骤7:算法正确性证明 如果odd_ count ≤ 1,字符串已经满足回文条件 如果odd_ count > 1,我们需要将多余的奇数个字符通过插入操作配对 每个插入操作可以减少一个奇数个字符 最终只需要保留一个奇数个字符(如果总长度为奇数)或0个(如果总长度为偶数) 步骤8:时间复杂度分析 统计频率:O(n),其中n是字符串长度 计算奇数个数:O(k),其中k是字符集大小 总时间复杂度:O(n),非常高效 这个解法利用了免费交换的特性,将复杂的位置调整问题简化为简单的频率统计问题。