奇怪的打印机问题(最小打印次数问题)
字数 1283 2025-11-09 08:26:24

奇怪的打印机问题(最小打印次数问题)

我将为你详细讲解"奇怪的打印机"问题,这是一个经典的区间动态规划问题。

题目描述

有一个奇怪的打印机,它有以下特殊打印规则:

  1. 每次打印时,打印机可以选择打印任意字符
  2. 打印机每次可以在任意起始位置和结束位置打印同一字符
  3. 新打印的字符会覆盖之前同一位置上的字符
  4. 打印机每次只能打印连续的相同字符

给定一个字符串s,你的任务是找出打印机打印出该字符串所需的最少打印次数。

示例:

  • 输入:s = "aaabbb"

  • 输出:2

  • 解释:首先打印"aaa",然后打印"bbb"

  • 输入:s = "aba"

  • 输出:2

  • 解释:首先打印"aaa",然后在中间位置打印"b"覆盖掉第二个a

解题思路分析

这个问题可以用区间动态规划来解决。我们需要定义状态,找到状态转移方程,然后进行求解。

详细解题步骤

步骤1:定义状态

定义dp[i][j]表示打印字符串s从位置i到位置j的子串所需的最少打印次数。

步骤2:寻找基本情况和状态转移

基本情况:

  • 当i = j时,只有一个字符,需要1次打印:dp[i][i] = 1

状态转移:
对于区间[i, j],我们有两种情况:

  1. 如果s[i] = s[j]

    • 我们可以在打印s[i]时一次性打印到位置j,这样打印s[j]时就不需要额外次数
    • 因此:dp[i][j] = dp[i][j-1]
  2. 如果s[i] ≠ s[j]

    • 我们需要将区间[i, j]分割成两个子区间[i, k]和[k+1, j]
    • 遍历所有可能的分割点k (i ≤ k < j)
    • dp[i][j] = min(dp[i][k] + dp[k+1][j]),对所有k取最小值

步骤3:算法实现

def strangePrinter(s):
    if not s:
        return 0
    
    n = len(s)
    # 创建dp表,初始化为最大值
    dp = [[float('inf')] * n for _ in range(n)]
    
    # 基本情况:单个字符需要1次打印
    for i in range(n):
        dp[i][i] = 1
    
    # 按区间长度从小到大计算
    for length in range(2, n + 1):  # 区间长度从2到n
        for i in range(n - length + 1):
            j = i + length - 1  # 区间结束位置
            
            # 情况1:首尾字符相同
            if s[i] == s[j]:
                dp[i][j] = dp[i][j - 1]
            else:
                # 情况2:首尾字符不同,遍历所有分割点
                for k in range(i, j):
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j])
    
    return dp[0][n - 1]

步骤4:示例分析

以s = "aba"为例:

  1. 初始化dp表:

    dp[0][0] = 1, dp[1][1] = 1, dp[2][2] = 1
    其他位置为无穷大
    
  2. 计算长度为2的区间:

    • [0,1]: s[0]='a' ≠ s[1]='b'
      dp[0][1] = min(dp[0][0] + dp[1][1]) = 1 + 1 = 2
    • [1,2]: s[1]='b' ≠ s[2]='a'
      dp[1][2] = min(dp[1][1] + dp[2][2]) = 1 + 1 = 2
  3. 计算长度为3的区间[0,2]:

    • s[0]='a' ≠ s[2]='a',但首尾相同!
    • 根据规则:dp[0][2] = dp[0][1] = 2
  4. 最终结果:dp[0][2] = 2

步骤5:优化思路

上述算法的时间复杂度是O(n³),我们可以进行优化:

  1. 预处理优化:如果字符串中有连续相同的字符,可以压缩成一个字符
  2. 记忆化搜索:使用递归+记忆化的方式实现
  3. 状态定义优化:重新定义状态转移方程

优化后的代码:

def strangePrinter(s):
    if not s:
        return 0
    
    # 预处理:合并连续相同字符
    compressed = []
    for char in s:
        if not compressed or char != compressed[-1]:
            compressed.append(char)
    s = ''.join(compressed)
    
    n = len(s)
    dp = [[0] * n for _ in range(n)]
    
    for i in range(n-1, -1, -1):
        dp[i][i] = 1
        for j in range(i+1, n):
            if s[i] == s[j]:
                dp[i][j] = dp[i][j-1]
            else:
                dp[i][j] = float('inf')
                for k in range(i, j):
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j])
    
    return dp[0][n-1]

总结

这个问题的关键在于理解:

  1. 当首尾字符相同时,可以节省一次打印操作
  2. 当首尾字符不同时,需要找到最优的分割点
  3. 区间动态规划按区间长度从小到大的顺序计算

通过这个例子,我们可以看到区间动态规划在解决这类"最优分割"问题上的强大能力。

奇怪的打印机问题(最小打印次数问题) 我将为你详细讲解"奇怪的打印机"问题,这是一个经典的区间动态规划问题。 题目描述 有一个奇怪的打印机,它有以下特殊打印规则: 每次打印时,打印机可以选择打印任意字符 打印机每次可以在任意起始位置和结束位置打印同一字符 新打印的字符会覆盖之前同一位置上的字符 打印机每次只能打印连续的相同字符 给定一个字符串s,你的任务是找出打印机打印出该字符串所需的最少打印次数。 示例: 输入:s = "aaabbb" 输出:2 解释:首先打印"aaa",然后打印"bbb" 输入:s = "aba" 输出:2 解释:首先打印"aaa",然后在中间位置打印"b"覆盖掉第二个a 解题思路分析 这个问题可以用区间动态规划来解决。我们需要定义状态,找到状态转移方程,然后进行求解。 详细解题步骤 步骤1:定义状态 定义dp[ i][ j ]表示打印字符串s从位置i到位置j的子串所需的最少打印次数。 步骤2:寻找基本情况和状态转移 基本情况: 当i = j时,只有一个字符,需要1次打印:dp[ i][ i ] = 1 状态转移: 对于区间[ i, j ],我们有两种情况: 如果s[ i] = s[ j] 我们可以在打印s[ i]时一次性打印到位置j,这样打印s[ j ]时就不需要额外次数 因此:dp[ i][ j] = dp[ i][ j-1 ] 如果s[ i] ≠ s[ j] 我们需要将区间[ i, j]分割成两个子区间[ i, k]和[ k+1, j ] 遍历所有可能的分割点k (i ≤ k < j) dp[ i][ j] = min(dp[ i][ k] + dp[ k+1][ j ]),对所有k取最小值 步骤3:算法实现 步骤4:示例分析 以s = "aba"为例: 初始化dp表: 计算长度为2的区间: [ 0,1]: s[ 0]='a' ≠ s[ 1 ]='b' dp[ 0][ 1] = min(dp[ 0][ 0] + dp[ 1][ 1 ]) = 1 + 1 = 2 [ 1,2]: s[ 1]='b' ≠ s[ 2 ]='a' dp[ 1][ 2] = min(dp[ 1][ 1] + dp[ 2][ 2 ]) = 1 + 1 = 2 计算长度为3的区间[ 0,2 ]: s[ 0]='a' ≠ s[ 2 ]='a',但首尾相同! 根据规则:dp[ 0][ 2] = dp[ 0][ 1 ] = 2 最终结果:dp[ 0][ 2 ] = 2 步骤5:优化思路 上述算法的时间复杂度是O(n³),我们可以进行优化: 预处理优化 :如果字符串中有连续相同的字符,可以压缩成一个字符 记忆化搜索 :使用递归+记忆化的方式实现 状态定义优化 :重新定义状态转移方程 优化后的代码: 总结 这个问题的关键在于理解: 当首尾字符相同时,可以节省一次打印操作 当首尾字符不同时,需要找到最优的分割点 区间动态规划按区间长度从小到大的顺序计算 通过这个例子,我们可以看到区间动态规划在解决这类"最优分割"问题上的强大能力。