[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之队列模拟算法(打印机问题)[首先研究这个问题作为开始] 任务队列 定义一个任务队列,来管理任务,而无需关心队列的”任务类型". # 自定义队列类 ...
随机推荐
- [bzoj1355][Baltic2009]Radio Transmission_KMP
Radio Transmissio bzoj-1355 Description 给你一个字符串,它是由某个字符串不断自我连接形成的. 但是这个字符串是不确定的,现在只想知道它的最短长度是多少. Inp ...
- 基于hi-nginx的web开发(python篇)——cookie和会话管理
hi-nginx通过redis管理会话. 要开启管理,需要做三件事. 第一件开启userid: userid on; userid_name SESSIONID; userid_domain loca ...
- java.text.DateFormat 多线程并发问题
在日常开发中,java.text.DateFormat 应该算是使用频率比较高的一个工具类,经常会使用它 将 Date 对象转换成字符串日期,或者将字符串日期转化成 Date 对象.先来看一段眼熟的代 ...
- 初始css
1.CSS规则由两部分构成,即选择器和声明器 声明必须放在{}中并且声明可以是一条或者多条 每条声明由一个属性和值构成,属性和值用冒号分开,每条语句用英文冒号分开 注意: css的最后一条声明,用以结 ...
- git项目初始化
Command line instructions 1.Git global setup git config --global user.name "99176942"git c ...
- ES5和ES6两个值的比较
ES5比较两个值是否相等 1)相等运算符 (==):比较两个数值是否相等,自动转换类型后再进行比较 2)全等运算符(===):比较两个比较值的数值和类型是否相等 ES5的特殊: ES6提出" ...
- 网络1711-1712的C语言作业总结(2017-2018第一学期)
1.第0次作业总结--预备作业 作业地址 1711班级总结 1712班级总结 2.第一次作业总结--顺序结构 作业地址 1711班级总结 1712班级总结 3.第二次作业总结--分支结构 作业地址 1 ...
- 凡事预则立-于Beta冲刺前
凡事预则立,在Beta开始前的描述 在Beta项目冲刺开始之前,我们小组组织了一次活动室的讨论,明确了一下分工和即将来临的Beta冲刺要处理的问题和需要继续改进的地方.顺带补上一直没有的照片: 针对几 ...
- 敏捷冲刺每日报告五(Java-Team)
第五天报告(10.29 周日) 团队:Java-Team 成员: 章辉宇(284) 吴政楠(286) 陈阳(PM:288) 韩华颂(142) 胡志权(143) github地址:https://gi ...
- python3.* socket例子
On Server: # -*- coding: utf-8 -*-#this is the server import socketif "__main__" == __name ...