LeetCode 664. 奇怪的打印机

很久没有做题了,这道题目很容易想到用 dp 去做,但是 dp 过程中有一些细节值得记录下来。

题目描述

有台奇怪的打印机有以下两个特殊要求:

  • 打印机每次只能打印由 同一个字符 组成的序列。
  • 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。
    给你一个字符串 s ,你的任务是计算这个打印机打印它需要的最少打印次数。
     
    示例1:
输入:s = "aaabbb"
输出:2
解释:首先打印 "aaa" 然后打印 "bbb"。

示例2:

输入:s = "aba"
输出:2
解释:首先打印 "aaa" 然后在第二个位置打印 "b" 覆盖掉原来的字符 'a'。

提示:

  • 1 <= s.length <= 100
  • s 由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/strange-printer

思路解析

动态规划的思路,我们用dp[i][j]记录打印s的第i位到第j位所需要的最小的次数。
考虑两种情况,
s[i] == s[j],那么我们可以用一次打印,把第i位至第j位全部打印为s[i],然后再考虑打印第i + 1位至第j位。
s[i] != s[j],我们需要记录一个中间值 k,分两段打印,分别是 dp[i][k]dp[k + 1][j]k的取值范围为(i, j),求对应的最小值即可。

\[\left\{ \begin{array}{**lr**} dp(i, j) = dp(i + 1, j) & s(i) = s(j) \\ dp(i, j) = \min_{k = i}^{j - 1}{dp(i, k) + dp(k + 1, j)} & s(i) \neq s(j) \end{array} \right. \]

这里值得注意的一点是,因为我们在求 dp[i][j] 时,需要用到介于 ij 之间的值,因此在求解时需要保证动态规划的计算过程满足无后效性,从后向前递推求解。

代码实现

class Solution {
public:
    int strangePrinter(string s) {
        int n = s.length();
        std::vector<std::vector<int>> dp(n, std::vector<int>(n));
        for(int i = n - 1; i > -1; i--) {
            dp[i][i] = 1;
            for(int j = i + 1; j < n; j++) {
                if(s[i] == s[j]) dp[i][j] = dp[i][j - 1];
                else {
                    int tmp = INT_MAX;
                    for(int k = i; k < j; k++)
                        tmp = std::min(tmp, dp[i][k] + dp[k + 1][j]);
                    dp[i][j] = tmp;
                }
            }
        }
        return dp[0][n - 1];
    }
};
posted @ 2021-05-24 12:44  行者橙子  阅读(133)  评论(0编辑  收藏  举报