package 左神题目.dp;
import java.util.*;
//给定一个字符串str,给定一个字符串类型的数组arr,
// 出现的字符都是小写英文arr每一个字符串,
// 代表一张贴纸,你可以把单个字符剪开使用,
// 目的是拼出str来返回需要至少多少张贴纸可以完成这个任务。
//例子:str="babac”,arr =("ba”,"c”,"abcd"
//至少需要两张贴纸“ba”和"abcd",因为使用这两张贴纸,
// 把每一个字符单独剪开,含有2个a、2个b、1个C。是可以拼出str的。所以返回2。
public class StickersToSpellWord {
//1.
public static int minStickers1(String[] stickers,String target){
int ans = process1(stickers,target);
return ans==Integer.MAX_VALUE? -1:ans;
}
//每一种贴纸有无穷张
//返回搞定target的最少张数
public static int process1(String[] stickers,String target){
if(target.length()==0){
return 0;
}
int min = Integer.MAX_VALUE;
for(String first:stickers){
String rest = minus(target,first);
//如果rest长度这个和原字符串一样,那么就是无效选择
if(rest.length() != target.length()){
min = Math.min(min,process1(stickers,rest));
}
}
//判断min是不是无效选择min==Integer.MAX_VALUE? 0:1;
//是的话就是Integer.MAX_VALUE+0 : 不是的话就是min + 1==>当前这张加上这张之后所有
return min + (min==Integer.MAX_VALUE? 0:1);
}
public static String minus(String s1,String s2){
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int[] count = new int[26];
for(char cha: str1){
count[cha - 'a']++;
}
for(char cha: str2){
count[cha - 'a']--;
}
//程序开发过程中,我们常常碰到字符串连接的情况,方便和直接的方式是通过“+”符号来实现,
// 但是这种方式达到目的的效率比较低,且每执行一次都会创建一个String对象,
// 即耗时,又浪费空间。使用StringBuilder类(就可以避免这种问题的发生)
//append(String str) / append(char c):字符串连接
//builder.append((char)("14124").append("111").append("2222");
StringBuilder builder = new StringBuilder();
for(int i = 0;i<26;i++){
if(count[i]>0){
for(int j =0;j<count[i];j++){
builder.append((char)(i+'a'));
}
}
}
return builder.toString();
}
//2.
public static int minStickers2(String[] stickers,String target){
int[][] counts = new int[stickers.length][26];
for(int i=0;i<stickers.length;i++){
char[] str = stickers[i].toCharArray();
for(char cha:str) {
counts[i][cha - 'a']++;
}
}
int ans = process2(counts,target);
return ans==Integer.MAX_VALUE? -1:ans;
}
//字符串不好确定参数可变范围,所以表依赖结构不好用也没必要用,当然也可以用,能做出了严格表依赖最好
public static int process2(int[][] stickers,String t){
if(t.length()==0){
return 0;
}
//t的词频
int[] count = new int[26];
char[] target = t.toCharArray();
for(char cha: target){
count[cha-'a']++;
}
int N = stickers.length;
int min = Integer.MAX_VALUE;
for(int i=0;i<N;i++){
//尝试第一张贴纸是谁
int[] sticker = stickers[i];
//判断包含剩余字符里面的第一个字符(随机位置的可以,最后一个,中间某一个)
//最关键的优化(重要的剪枝!这一步也是贪心)
if(sticker[target[0]-'a']>0){// == rest.length() != target.length()
StringBuilder builder = new StringBuilder();
for(int j=0;j<26;j++){
if(count[j]>0){
//看看有没有剩余
int nums = count[j]-sticker[j];
for(int k=0;k<nums;k++){
builder.append((char)(j+'a'));
}
}
}
//转格式
String rest = builder.toString();
min = Math.min(min,process2(stickers,rest));
}
}
return min+(min==Integer.MAX_VALUE?0:1);
}
//3.缓存表
public static int minStickers3(String[] stickers,String target){
int[][] counts = new int[stickers.length][26];
for(int i=0;i<stickers.length;i++){
char[] str = stickers[i].toCharArray();
for(char cha:str) {
counts[i][cha - 'a']++;
}
}
HashMap<String,Integer> dp =new HashMap<>();
dp.put("",0);
int ans = process3(counts,target,dp);
return ans==Integer.MAX_VALUE? -1:ans;
}
public static int process3(int[][] stickers,String t,HashMap<String,Integer> dp){
//看看t这个字符串有没有计算过
if(dp.containsKey(t)){
return dp.get(t);
}
char[] target = t.toCharArray();
int[] tcounts = new int[26];
for(char cha: target){
tcounts[cha - 'a']++;
}
int N = stickers.length;
int min = Integer.MAX_VALUE;
for(int i = 0;i<N;i++){
int[] sticker = stickers[i];
if(sticker[target[0]-'a']>0){
StringBuilder builder = new StringBuilder();
for(int j = 0;j<26;j++){
if(tcounts[j]>0){
int nums = tcounts[j] - sticker[j];
for(int k=0;k<nums;k++) {
builder.append((char) (j + 'a'));
}
}
}
String rest = builder.toString();
min = Math.min(min,process3(stickers,rest,dp));
//保存rest这个字符串的最少贴纸
dp.put(rest,min);
}
}
return min+(min==Integer.MAX_VALUE?0:1);
}
public static void main(String[] args){
String[] s={"ab","abd","abcd","sdfsd","p"};
String target="babacsp";
System.out.println(minStickers1(s,target));
System.out.println(minStickers2(s,target));
System.out.println(minStickers3(s,target));
}
}
给定一个字符串str,给定一个字符串类型的数组arr,/出现的字符都是小写英文arr每一个字符串,/代表一张贴纸,你可以把单个字符剪开使用
最新推荐文章于 2023-05-28 10:28:55 发布