LeetCode664. Strange Printer

There is a strange printer with the following two special requirements:ide

  1. The printer can only print a sequence of the same character each time.
  2. 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.ui

Example 1:this

Input: "aaabbb"
Output: 2
Explanation: Print "aaa" first and then print "bbb".

Example 2:spa

Input: "aba"
Output: 2
Explanation: Print "aaa" first and then print "b" from the second place of the string, which will cover the existing c

分析code

这类题目的本质是寻找作某件事在没有特定步骤的情形下总共有多少种实现方法,能够经过遍历全部可能来解决,是一个典型的dp问题。blog

dp[i][j] 表明变成string中从index i 到 index j 部分须要的最少print次数。 那么有:索引

  • dp[i][i] = 1: we need 1 turn to paint a single character.
  • dp[i][i + 1]
    • dp[i][i + 1] = 1 if s.chartAt(i) == s.charAt(i + 1)  
    • dp[i][i + 1] = 2 if s.chartAt(i) != s.charAt(i + 1)

Then we can iteration len from 2 to possibly n. For each iteration, we iteration start index from 0 to the farthest possible.ci

  • The maximum turns for dp[start][start + len] is len + 1, i.e. print one character each time.
  • We can further divide the substring to two parts: start -> start+k and start+k+1 -> start+len. It is something as following:
    index |start ... start + k| |start + k + 1 ... start + len| char | a ... b | | c ... b | 
    • As shown above, if we have s.charAt(start + k) == s.charAt(start + len), we can make it in one turn when we print this character (i.e. b here)
    • This case we can reduce our turns to dp[start][start + k] + dp[start + k + 1][start + len] - 1

难理解的部分来了,首选对于 dp[start][start+len] 的最大值确定是len+1, 也就是每次只print一个字符。rem

须要注意的几点是:1. 每次打印一个字符或者是相同字符的序列,这能够推出若是一个字符串里出现了一个不一样的字符,那么至少要为这个字符打印一次。字符串

         2. 由于每次的打印能够选择任何位置,能够覆盖原有字符

         3. 这是个从无到有,而后再去替换的过程

能够将substring分红两部分,start -> start+k and start+k+1 -> start+len,以索引k做为分割,若是 start+k 处的字符和 start+len初的字符相同,那当咱们在前面打印这个字符b时能够选择一次性打印连续个b,这样在对于dp[start + k + 1][start + len]来讲至关于减小了一次打印b的过程,因此 dp[start][start+len] 就被分解成了子问题 dp[start][start + k] + dp[start + k + 1][start + len] - 1。

代码

class Solution { public int strangePrinter(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); int[][] dp = new int[n][n]; for (int i = 0; i < n; i++) { dp[i][i] = 1; if (i < n - 1) { dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1) ? 1 : 2; } } for (int len = 2; len < n; len++) { for (int start = 0; start + len < n; start++) { dp[start][start + len] = len + 1; for (int k = 0; k < len; k++) { int temp = dp[start][start + k] + dp[start + k + 1][start + len]; dp[start][start + len] = Math.min( dp[start][start + len], s.charAt(start + k) == s.charAt(start + len) ? temp - 1 : temp ); } } } return dp[0][n - 1]; } }
相关文章
相关标签/搜索