LeetCode:691. Stickers to Spell Word

从暴力递归到动态规划5

LeetCode题目链接:https://leetcode.com/problems/stickers-to-spell-word/

目录

题目

暴力递归

动态规划


题目五

        给定一个字符串str,给定一个字符串类型的数组arr,出现的字符都是小写英文arr每一个字符串,代表一张贴纸,你可以把单个字符剪开使用,目的是拼出str来返回需要至少多少张贴纸可以完成这个任务。(每种贴纸数量是无限的

例子:str= "babac". arr = {"ba" "c" ,"abcd""} 至少需要两张贴纸""ba"和""abcd",因为使用这两张贴纸,把每一个字符单独剪开,含有2个a、2个b、1个c。是可以拼出str的。所以返回2。

暴力递归

  • 遍历每一种情况,返回一个最小值;若找不到合适的贴纸,返回0;

  • 每次调用一个贴纸,返回减去该贴纸字母后的剩余字符串;

  • 当剩余字符串长度为0时,贴纸调用完毕,返回所用贴纸数;

  • 求得最小花费贴纸数;

代码如下

	//暴力递归
    public static int sticker (String[] str, String target) {
        if (str == null || str.length==0 || target.length()==0) {
            return -1;
        }
        int min = process(str,target);
        return min == Integer.MAX_VALUE ? -1 : min;

    }
    public static int process(String[] str, String target) {
        if (target.length() == 0) {
            return 0;
        }
        int min = Integer.MAX_VALUE;
        for (String s: str) {
            //返回target减去s后的剩余字符串
            String rest = minus(target, s);
            //若剩余字符串与目标字符串相等,说明该贴纸中不包含所需字母;
            //反之,递归剩余字符串;
            if (rest.length() != target.length()) {
                min = Math.min(min,process(str,rest));
            }
        }
        return min == Integer.MAX_VALUE ? Integer.MAX_VALUE : min+1;
    }

其中minus是一个自定义方法,传入目标字符串和贴纸s;返回目标字符串减去贴纸s后的剩余字符串。

minus方法

	public static String minus (String init, String s) {
        char[] inits = init.toCharArray();
        char[] chars = s.toCharArray();
        int[] counts = new int[26];
        for (char s1: inits) {
            counts[s1-'a']++;
        }
        for (char s2: chars) {
            counts[s2-'a']--;
        }
        String ans = "";
        for (int i = 0; i < counts.length; i++) {
            while (counts[i] > 0) {
                ans += (char)(i+'a');
                counts[i]--;
            }
        }
        return ans;
    }

测试

	public static void main(String[] args) {
        String[] strings = {"with","example","science"};
        System.out.println(sticker(strings,"thehat"));
    }
    //输出
    3

动态规划

        上述的递归方法在速度和效率上过于低下。我们采用一个二维数组“剪枝”的方法对贴纸进行筛选;减少无用贴纸的计算,增加有效贴纸的命中率,尽可能快的算出花费贴纸的最小数值。

  • 二维数组:每行对应一个贴纸,每行有26列对应a~z。记录每个贴纸每个字母的数量。

  • 剪枝:当且仅当第一个贴纸包含目标字符串所含任一字母时(有效贴纸),继续选取贴纸;

  • 若第一个贴纸不包含任何一个目标字符串中的字母,则直接丢弃(无效贴纸)。因为以该贴纸为首向下查询所得的值一定不是最小值

代码如下

//动态规划
    public static int sticker1 (String[] str, String target) {
        if (str == null || str.length==0 || target.length()==0) {
            return -1;
        }
        int N = str.length;
        int[][] stickers = new int[N][26];
        //二维数组赋值;
        for (int i = 0; i < N; i++) {
            char[] chars = str[i].toCharArray();
            for (char c: chars) {
                stickers[i][c-'a']++;
            }
        }
        int min = process1(stickers,target);
        return min == Integer.MAX_VALUE ? -1 : min;
    }
    public static int process1(int[][] sti, String t) {
        if (t.length()==0) {
            return 0;
        }
        int min = Integer.MAX_VALUE;
        //target的词频数组
        int[] tcounts = new int[26]; 
        char[] target = t.toCharArray();
        for (char tar: target) {
            tcounts[tar-'a']++;
        }
        int N = sti.length;
        for (int i = 0; i < N; i++) {
            //选取每个贴纸当做第一个贴纸使用。
            int[] sticker = sti[i];
            //最关键的一步!!剪枝!
            //判断第一个贴纸是否可用。
            if (sticker[target[0]-'a'] > 0) {
                String rest = "";
                for (int j = 0; j < 26; j++) {
                    if (tcounts[j]>0){
                        //减去可用贴纸后的剩余字符串;
                        int nums = tcounts[j] - sticker[j];
                        while (nums > 0) {
                            rest += (char)(j+'a');
                            nums--;
                        }
                    }
                }
                min = Math.min(min,process1(sti,rest));
            }
        }
        return min == Integer.MAX_VALUE ? Integer.MAX_VALUE : min+1;

    }

测试

	public static void main(String[] args) {
        String[] strings = {"with","example","science"};
        System.out.println(sticker(strings,"thehat"));
        System.out.println(sticker1(strings,"thehat"));
    }
	//输出
	3
	3

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superzheng__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值