【刷题篇】贴纸拼词

一、题目

我们有 n 种不同的贴纸。每个贴纸上都有一个小写的英文单词。

您想要拼写出给定的字符串 target ,方法是从收集的贴纸中切割单个字母并重新排列它们。如果你愿意,你可以多次使用每个贴纸,每个贴纸的数量是无限的。

返回你需要拼出 target 的最小贴纸数量。如果任务不可能,则返回 -1 。
OJ链接

二、题解

2.1 方法一:递归尝试

递归函数:
stickers :表示已有的贴纸
target:目标字符串
终止条件:
当target.length==0时,意味着目标字符串长度为0,那么直接返回0张贴纸

先假设每张贴张都作为第一张贴纸,使用minus函数剪掉target中的和first中的共有的字符,得到的rest作为下一次调用的target
如果rest的长度和target的长度相同,说明target中和当前的first中不存在重复的字符,就去尝试下一张贴纸作为第一张提位置的情况

返回需要的最少的贴纸时,如果是有效的解,需要加上使用的第一张贴纸
在这里插入图片描述
源码:
👿

   public static int minStickers1(String[] stickers, String target) {
  if(target==null||target.length()==0){
      return 0;
  }
        int ans=process1(stickers,target);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
    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);
            if(target.length()!=rest.length()){
           min=Math.min(min,process1(stickers,rest));
          }
            }
            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 ch:str1) {
            count[ch-'a']++;
        }
        for (char ch:str2) {
            count[ch-'a']--;
        }
        StringBuilder sb=new StringBuilder();
        for (int i = 0; i <count.length ; i++) {
            if(count[i]>0){
                while(count[i]>0){
                    sb.append(i+'a');
                    count[i]--;
                }
            }

        }
        return sb.toString();
    }

2.2方法二:剪枝

思路:

  1. 用一个二维数组来存储所有的贴纸
    在这里插入图片描述
  2. 在方法一中,依次使用每一张贴纸都作为第一张贴纸去尝试,该方法的优化是选所有贴纸中具有目标贴纸的第一个字符的贴纸作为第一张贴纸去往下尝试,因为对于目标贴纸中的第一个字符在后续中总是要消掉的,放在前面先消掉,不会对结果有影响。(这样就实现了剪枝)
    在这里插入图片描述

源码:
😇

public static int process2(int[][] stickers,String target){
    if(target.length()==0) {
        return 0;
    }
          char[] t=target.toCharArray();
          int[] tcounts=new int[26];
        for (char ch:t) {
            tcounts[ch-'a']++;
        }
       int N=stickers.length;
        int min=Integer.MAX_VALUE;
        for (int i = 0; i < N; i++) {
            int[] sticker=stickers[i];
            //剪枝
            if(sticker[t[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,process2(stickers,rest));
            }
        }
    return  min+(min==Integer.MAX_VALUE?0:1);
    }
    
    public static int minStickers2(String[] stickers, String target) {
        int  N=stickers.length;
        int[][] counts=new int[N][26];
        //生成所有贴纸的词频统计
        for (int i = 0; i < N; i++) {
            char[] str=stickers[i].toCharArray();
            for (char ch:str) {
                counts[i][ch-'a']++;
            }
        }
        int ans=process2(counts,target);
        return ans==Integer.MAX_VALUE?-1:ans;
    }

2.3 方法三:缓存表

在方法二的基础上多添加了一个缓存表

源码:
😐

public static int process3(int[][] stickers, String target, HashMap<String,Integer> dp) {
        if(dp.containsKey(target)){
            return dp.get(target);
        }
        if (target.length() == 0) {
            return 0;
        }
        char[] t=target.toCharArray();
        int[] tcounts=new int[26];
        for (char ch:t) {
            tcounts[ch-'a']++;
        }
        int N=stickers.length;
        int min=Integer.MAX_VALUE;
        for (int i = 0; i < N; i++) {
            int[] sticker=stickers[i];
            //剪枝
            if(sticker[t[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));
            }
        }
        int ans=min+(min==Integer.MAX_VALUE?0:1);
        dp.put(target,ans);
        return  ans;
    }
    public static int minStickers3(String[] stickers, String target) {
        int  N=stickers.length;
        int[][] counts=new int[N][26];
        //生成所有贴纸的词频统计
        for (int i = 0; i < N; i++) {
            char[] str=stickers[i].toCharArray();
            for (char ch:str) {
                counts[i][ch-'a']++;
            }
        }
        HashMap<String,Integer> dp=new HashMap<>();
        dp.put("",0);
        int ans=process3(counts,target,dp);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值