题目描述(传送门)
有台奇怪的打印机有以下两个特殊要求:
打印机每次只能打印由 同一个字符 组成的序列。
每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。
给你一个字符串 s ,你的任务是计算这个打印机打印它需要的最少打印次数。
示例 1:
输入:s = "aaabbb"
输出:2
解释:首先打印 "aaa" 然后打印 "bbb"。
示例 2:
输入:s = "aba"
输出:2
解释:首先打印 "aaa" 然后在第二个位置打印 "b" 覆盖掉原来的字符 'a'。
解题思路
首先我们来看看例子:
比如说是字符串 abab
- 打印字符串a的时候,只需要打印一次。
- 打印字符串ab的时候,需要打印两次,也就是在1的基础上在打印一次。
- 打印字符串aba的时候,需要打印两次,也是在1的基础上在打印一次。(两边字符相同,最后的字符也就不要单独打印一次,在打印开头的时候就直接打印出最后边的字符了,也就是先打印aaa 在打印aba)
- 打印字符串abab的时候,需要打印三次,(两边字符不相同就需要就行字符串的拆分,a+bab或者 ab+ab 或者 aba +b),在这里选择aba+b这种拆分方式,最终是应该打印三次。
动态规划
状态定义:dp [i] [ j] 代表的是字符串在区间【i,j】中需要的最少打印次数。
初始化;dp[i][i] =1
状态方程:
返回值:dp[0][n-1]
代码
import java.util.Arrays;
/**
* @ClassName Main
* @Description :TODO
* @Author Josvin
* @Date 2021/05/24/11:47
*/
public class Main {
public static void main(String[] args) {
System.out.println(strangePrinter("abcc"));
}
public static int strangePrinter(String s) {
int n = s.length();
int[][] dp = new int[n][n];
// dp[i][j] 表示打印完成区间【i,j】的最少操作数
for (int i = n - 1; i >= 0; i--) {
// 初始化 dp[i][i] = 1
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {
// 两边字符相等的情况
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i][j - 1];
} else {
// 两边字符不相等的情况
int minNum = Integer.MAX_VALUE;
// System.out.println("============================");
for (int k = i; k < j; k++) {
minNum = Math.min(minNum, dp[i][k] + dp[k + 1][j]);
// System.out.println("[i="+i+" k="+k +"][k+1="+(k+1)+" j="+j+"]");
}
dp[i][j] = minNum;
}
for (int m = 0; m < dp.length; m++) {
System.out.println(Arrays.toString(dp[m]));
}
System.out.println("==============");
}
/*for (int j = 0; j < dp.length; j++) {
System.out.println(Arrays.toString(dp[j]));
}*/
}
return dp[0][n - 1];
}
}
看看过程: