[LeetCode] Strange Printer 奇怪的打印机
There is a strange printer with the following two special requirements:
- The printer can only print a sequence of the same character each time.
- At each turn, the printer can print new characters starting from and ending at any places, and will cover the original existing characters.
Given a string consists of lower English letters only, your job is to count the minimum number of turns the printer needed in order to print it.
Example 1:
Input: "aaabbb"
Output: 2
Explanation: Print "aaa" first and then print "bbb".
Example 2:
Input: "aba"
Output: 2
Explanation: Print "aaa" first and then print "b" from the second place of the string, which will cover the existing character 'a'.
Hint: Length of the given string will not exceed 100.
这道题说有一种奇怪的打印机每次只能打印一排相同的字符,然后可以在任意起点和终点位置之间打印新的字符,用来覆盖原有的字符。现在给了我们一个新的字符串,问我们需要几次可以正确的打印出来。题目中给了两个非常简单的例子,主要是帮助我们理解的。博主最开始想的方法是一种类似贪婪算法,先是找出出现次数最多的字符,然后算需要多少次变换能将所有其他字符都变成那个出现最多次的字符,结果fail了。然后又试了一种类似剥洋葱的方法,从首尾都分别找连续相同的字符,如果首尾字符相同,则两部分一起移去,否则就移去连续相同个数多的子序列,这种基于贪婪算法的解法还是fail了,所以这道题是典型的只能动态规划Dynamic Programming,而不能用贪婪算法Greedy Algorithm的题。这道题的解题思路跟之前那道Remove Boxes很相似,博主在那个帖子中做了详细的讲解,是根据fun4leetcode大神的帖子写的,大神的思路对解这道题也相当有帮助。其实这道题并没有之前那道Remove Boxes难,移除盒子的题有隐含的条件需要加到重现关系中,大大地增加了题目的难度,非常地难想出来,这道题没有隐含条件都是个Hard题,那道题妥妥应该是Super Hard。
好,话不多说,来分析这道题吧。思考的线索和思路很重要,不理解核心精髓,当背题侠是没用的,稍微变个形式又不会了,博主就经常是这样的-.-!!!。既然说了要用DP来做,先整个二维dp数组呗,其中dp[i][j]表示打印出字符串[i, j]范围内字符的最小步数,难点就是找递推公式啦。遇到乍看去没啥思路的题,博主一般会先从简单的例子开始,看能不能分析出规律,从而找到解题的线索。首先如果只有一个字符,比如字符串是"a"的话,那么直接一次打印出来就行了。如果字符串是"ab"的话,那么我们要么先打印出"aa",再改成"ab",或者先打印出"bb",再改成"ab"。同理,如果字符串是"abc"的话,就需要三次打印。那么一个很明显的特征是,如果没有重复的字符,打印的次数就是字符的个数。燃鹅这题的难点就是要处理有相同字符的情况,比如字符串是"aba"的时候,我们先打"aaa"的话,两步就搞定了,如果先打"bbb"的话,就需要三步。我们再来看一个字符串"abcb",我们知道需要需要三步,我们看如果把这个字符串分成两个部分"a"和"bcb",它们分别的步数是1和2,加起来的3是整个的步数。而对于字符串"abba",如果分成"a"和"bba",它们分别的步数也是1和2,但是总步数却是2。这是因为分出的"a"和"bba"中的最后一个字符相同。对于字符串"abbac",因为位置0上的a和位置3上的a相同,那么整个字符串的步数相当于"bb"和"ac"的步数之和,为3。那么分析到这,是不是有点眉目了?我们关心的是字符相等的地方,对于[i, j]范围的字符,我们从i+1位置上的字符开始遍历到j,如果和i位置上的字符相等,我们就以此位置为界,将[i+1, j]范围内的字符拆为两个部分,将二者的dp值加起来,和原dp值相比,取较小的那个。所以我们的递推式如下:
dp[i][j] = min(dp[i][j], dp[i + ][k - ] + dp[k][j] (s[k] == s[i] and i + <= k <= j)
要注意一些初始化的值,dp[i][i]是1,因为一个字符嘛,打印1次,还是就是在遍历k之前,dp[i][j]初始化为 1 + dp[i + 1][j],为啥呢,可以看成在[i + 1, j]的范围上多加了一个s[i]字符,最坏的情况就是加上的是一个不曾出现过的字符,步数顶多加1步,注意我们的i是从后往前遍历的,当然你可以从前往后遍历,参数对应好就行了,参见代码如下:
解法一:
class Solution {
public:
int strangePrinter(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, ));
for (int i = n - ; i >= ; --i) {
for (int j = i; j < n; ++j) {
dp[i][j] = (i == j) ? : ( + dp[i + ][j]);
for (int k = i + ; k <= j; ++k) {
if (s[k] == s[i]) dp[i][j] = min(dp[i][j], dp[i + ][k - ] + dp[k][j]);
}
}
}
return (n == ) ? : dp[][n - ];
}
};
理解了上面的DP的方法,那么也可以用递归的形式来写,记忆数组memo就相当于dp数组,整个思路完全一样,参见代码如下:
解法二:
class Solution {
public:
int strangePrinter(string s) {
int n = s.size();
vector<vector<int>> memo(n, vector<int>(n, ));
return helper(s, , n - , memo);
}
int helper(string s, int i, int j, vector<vector<int>>& memo) {
if (i > j) return ;
if (memo[i][j]) return memo[i][j];
memo[i][j] = helper(s, i + , j, memo) + ;
for (int k = i + ; k <= j; ++k) {
if (s[k] == s[i]) {
memo[i][j] = min(memo[i][j], helper(s, i + , k - , memo) + helper(s, k, j, memo));
}
}
return memo[i][j];
}
};
类似题目:
参考资料:
https://discuss.leetcode.com/topic/100137/java-solution-dp
https://discuss.leetcode.com/topic/100212/c-29ms-dp-solution
https://discuss.leetcode.com/topic/100135/java-o-n-3-short-dp-solution
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Strange Printer 奇怪的打印机的更多相关文章
- LeetCode 664. Strange Printer 奇怪的打印机(C++/Java)
题目: There is a strange printer with the following two special requirements: The printer can only pri ...
- [Swift]LeetCode664. 奇怪的打印机 | Strange Printer
There is a strange printer with the following two special requirements: The printer can only print a ...
- Leetcode 664.奇怪的打印机
奇怪的打印机 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印同一个字符序列. 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符. 给定一个只包含小写英文字母的字符串,你的任 ...
- leetcode 664. Strange Printer
There is a strange printer with the following two special requirements: The printer can only print a ...
- Java实现 LeetCode 664 奇怪的打印机(DFS)
664. 奇怪的打印机 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印同一个字符序列. 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符. 给定一个只包含小写英文字母的字符 ...
- LeetCode664. Strange Printer
There is a strange printer with the following two special requirements: The printer can only print a ...
- HDU 1548 A strange lift 奇怪的电梯(BFS,水)
题意: 有一座电梯,其中楼层从1-n,每层都有一个数字k,当处于某一层时,只能往上走k层,或者下走k层.楼主在a层,问是否能到达第b层? 思路: 在起点时只能往上走和往下走两个选择,之后的每层都是这样 ...
- 664. Strange Printer
class Solution { public: int dp[100][100]; int dfs(const string &s, int i,int j) { if(i>j)ret ...
- [LeetCode] 由 “打印机任务队列" 所想
一.这是个基础问题 Ref: Python之队列模拟算法(打印机问题)[首先研究这个问题作为开始] 任务队列 定义一个任务队列,来管理任务,而无需关心队列的”任务类型". # 自定义队列类 ...
随机推荐
- Konckout第六个实例:自定义组件 -- 发表评论
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- Mybatis 常用标签
MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格,还要注意省掉 ...
- html5 geolocation配合百度地图api实现定位
1.了解html5 geolocation HTML5 Geolocation(地理定位)用于定位用户的位置.鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的.=> 使用时 ...
- python学习笔记1——基础
1)注释 单行注释以#开头,例:#第一个注释 多行注释可用多个#号,还有'''和""",例: #第一行注释 #第二行注释 ''' 第三行注释 第四行注释 ''' &quo ...
- Redux----Regular的Redux实现整理
Regular的Redux实现整理 什么问题? 组件的树形结构决定了数据的流向,导致的数据传递黑洞 怎么解决? 所有组件都通过中介者传递共享数据 方案: 中介者: (function create ...
- MySQL之连接查询
主要是多表查询和连接查询
- 从0开始的LeetCode生活—9. Palindrome Number(回文数)
题目大意: 判断输入的数字是不是回文数.所谓回文数就是正反读都一样的数字,比如说11,121,1221这样子的数字.负数不会是回文数. 解题思路: 思路一:如果这个数是负数,则返回false,否则用一 ...
- 每日冲刺报告-Day3
敏捷冲刺每日报告--Day3 情况简介 今天的任务是把json处理函数加入到爬虫中,把搜索到的结果存到json文件里去. 任务进度 赵坤:在爬虫中加入了json处理的代码,解决了在控制台打印中文列表/ ...
- Flask 扩展 Mail
安装 pip install flask-mail from flask import Flask from flask_mail import Mail, Message app = Flask(_ ...
- [Android]上传到多个Maven仓库的Gradle插件RapidMavenPushPlugin
博客搬迁至https://blog.wangjiegulu.com RSS订阅:https://blog.wangjiegulu.com/feed.xml RapidMavenPushPlugin 用 ...