拼贴纸问题

问题描述

给定一个字符串str,给定一个字符串数组arr;
arr里的每一个字符串代表一张贴纸,可以把单个字符剪开使用,
目的是拼出字符串str,求至少需要多少张贴纸可以拼出字符串str。
例:str = "babac",arr = {"ba", "c", "abcd"}
至少需要两张贴纸,分别是“ba”和“abcd”,使用这两张贴纸,把每个字符单独剪开,
有2个a,2个b,1个c,可以拼出babac,所以结果是2。

题解

/**
 * @author IT00ZYQ
 * @date 2021/4/10 19:31
 **/
public class 拼贴纸 {

    public static int way1(String str, String[] arr) {
        // 将每个贴纸以数组的形式存储
        int[][] map = new int[arr.length][26];
        for (int i = 0; i < arr.length; i++) {
            for (char c : arr[i].toCharArray()) {
                map[i][c - 'a'] ++;
            }
        }
        return wayFunc1(str, map);
    }

    /**
     * @param rest 当前剩余的需要拼的字符,可变变量
     * @param map 每个贴纸的字符及其数量
     * @return 拼出rest字符串需要的最少贴纸数
     */
    private static int wayFunc1(String rest, int[][] map) {
        // 需要拼的字符串已经为空字符串,不再需要任何贴纸
        if ("".equals(rest)) {
            return 0;
        }
        // 将rest字符串也转化成数组
        int[] restMap = new int[26];
        for (char c : rest.toCharArray()) {
            restMap[c - 'a'] ++;
        }

        int minNext = Integer.MAX_VALUE;
        // 遍历所有贴纸
        for (int i = 0; i < map.length; i++) {
            // 只寻找包含rest字符串首字符的贴纸
            if (map[i][rest.charAt(0) - 'a'] == 0) {
                continue;
            }
            // 将rest - arr[i]剩余的字符串保存到sb中
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < 26; j++) {
                for (int k = 0; k < restMap[j] - map[i][j]; k++) {
                    sb.append((char)(j+'a'));
                }
            }
            // rest - arr[i]剩下的字符串需要的最少贴纸数
            minNext = Math.min(wayFunc1(sb.toString(), map), minNext);
        }
        // 当minNext为Integer.MAX_VALUE表示贴纸无法拼出字符串rest
        // 直接返回-1
        return minNext == Integer.MAX_VALUE ? -1 : (minNext + 1);
    }

    public static void main(String[] args) {
        String str = "abcccccdddddbbbaaaaa";
        String[] arr = {"aaaa", "bbaa", "ccddd"};

        System.out.println(way1(str, arr));
    }
}