给定一个字符串str,给定一个字符串类型的数组arr,/出现的字符都是小写英文arr每一个字符串,/代表一张贴纸,你可以把单个字符剪开使用

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));
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值