[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之队列模拟算法(打印机问题)[首先研究这个问题作为开始] 任务队列 定义一个任务队列,来管理任务,而无需关心队列的”任务类型". # 自定义队列类 ...
随机推荐
- java中有关流操作的类和接口
一.java操作l流有关的类和接口 1.File 文件类 2.RandomAccessFile 随机存储文件类 3.InputStream 字节输入流 4.OutputStream 字节输出流 5.R ...
- [Android]利用run-as命令在不root情况下读取data下面的数据
正文 一.关键步骤 主要是run-as命令: over@over-ThinkPad-R52:~$ adb shell $ run-as com.package $ cd /data/data/co ...
- Linux环境下Swap配置方法
Linux环境下Swap配置方法 场景: 今天下午安装一个CentOS6.5操作系统,忘记配置swap分区.看看如何安装系统之后,增加和删除swap分区.方法如下:1.内存占用情况[root@josh ...
- Mybatis学习笔记一
Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为M ...
- Leetcode 3——Palindrome Number(回文数)
Problem: Determine whether an integer is a palindrome. Do this without extra space. 简单的回文数,大一肯定有要求写过 ...
- JavaScript(第三十二天)【Ajax】
2005年Jesse James Garrett发表了一篇文章,标题为:"Ajax:A new Approach to Web Applications".他在这篇文章里介绍了一种 ...
- String [] 转 List<String>
整理笔记:String [] 转 List<String> String [] al = new String[]{"1","q","a& ...
- 20145237 实验五《Java网络编程》
20145237 实验五<Java网络编程> 一.实验内容 •1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: •2.利用加解密代码包,编译运行代码,一人加密,一人解密: •3 ...
- 【iOS】swift init构造器
这几天在使用 Swift 重写原来的一个运动社交应用 SportJoin. 为什么要重写呢? 首先因为实在找不到设计师给我作图; 其次, 我也闲不下来, 想找一些项目做, 所以只好将原来的代码重写了. ...
- 算法第四版学习笔记之快速排序 QuickSort
软件:DrJava 参考书:算法(第四版) 章节:2.3快速排序(以下截图是算法配套视频所讲内容截图) 1:快速排序 2: