很久没有做题了,这道题目很容易想到用 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{
egin{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)
eq s(j)
end{array}
ight.
]
这里值得注意的一点是,因为我们在求 dp[i][j]
时,需要用到介于 i
和 j
之间的值,因此在求解时需要保证动态规划的计算过程满足无后效性,从后向前递推求解。
代码实现
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];
}
};