Stickers to Spell Word:多个单词拆拼接成完整以个目标单词所耗最小数目

来源:互联网 发布:小米3支持联通4g网络吗 编辑:程序博客网 时间:2024/05/17 18:26

We are given N different types of stickers. Each sticker has a lowercase English word on it.

You would like to spell out the given target string by cutting individual letters from your collection of stickers and rearranging them.

You can use each sticker more than once if you want, and you have infinite quantities of each sticker.

What is the minimum number of stickers that you need to spell out the target? If the task is impossible, return -1.

Example 1:

Input:

["with", "example", "science"], "thehat"

Output:

3

Explanation:

We can use 2 "with" stickers, and 1 "example" sticker.After cutting and rearrange the letters of those stickers, we can form the target "thehat".Also, this is the minimum number of stickers necessary to form the target string.

Example 2:

Input:

["notice", "possible"], "basicbasic"

Output:

-1

Explanation:

We can't form the target "basicbasic" from cutting letters from the given stickers.

Note:

  • stickers has length in the range [1, 50].
  • stickers consists of lowercase English words (without apostrophes).
  • target has length in the range [1, 15], and consists of lowercase English letters.
  • In all test cases, all words were chosen randomly from the 1000 most common US English words, and the target was chosen as a concatenation of two random words.
  • The time limit may be more challenging than usual. It is expected that a 50 sticker test case can be solved within 35ms on average.

  • 思路:

    a)这是一道动态规划题目,因为:以sticker = “axc”、“ber”,target=“ab”为例

    若count(a)已知,即表示a所需sticker已知,那么

    count(“ab”)=MIN.{count(a)+ count(b|axc),count(a)+count(b|ber)}。

    b)使用位图法表示target的所有子集,比图target=“ab”,那么使用二进制的两位可以表示其所有子集   :   二进制数字:子target

    00:“”;  01:“b”  ;10:“a”; 11:“ab”,即范围在[0,1<<target.length)范围内,因为题目中说target最长15位,所以int类型范围够用。


    public int minStickers(String[] stickers, String target) {if (stickers.length <= 0)return -1;int n = target.length();int m = 1 << n;int[] d = new int[m];// 用数组d[]表示组合所有target子集所需sticker的数目,完整表示target的m-1;for (int i = 0; i < m; i++) {d[i] = Integer.MAX_VALUE;}d[0] = 0;// 从最小的子集况开始,根据每一个sticker从小到大开始组合for (int i = 0; i < m; i++) {// 对于target的每一种子集:如target=“ab”,那么“”、“a”、“b”、“ab”均是其子集if (d[i] == Integer.MAX_VALUE)continue;// 从该子集就无法再扩展,跳过for (String sticker : stickers) {int parent = i;// 对于每个子集,寻找可以有sticker拼接出的扩展子集char[] clist = sticker.toCharArray();for (char c : clist) {for (int j = 0; j < n; j++) {if (c == target.charAt(j) && ((parent >> j) & 1) == 0) {// 目标可以有该sticker拼接出,而且这个位置是新拼接出来的parent = parent | (1 << j);// 记录下拼接后的结果,再看看是用该sticker还能否拼接出更多位置break;}}}d[parent] = Math.min(d[parent], d[i] + 1);// 由该sticker新拼接出的集合是否比之前由其他sticker拼接所耗更小,或者就是比上个子集i多用了一个sticker}}return (d[m - 1] == Integer.MAX_VALUE) ? -1 : d[m - 1]; // d[m-1]就是完整的target}

    我啰嗦一大推也没太说清楚,就是先看看根据1个sticker能拼成什么最接近target的单词,

    在这个单词基础上,拿一个sticker看看下一个更接近target的单词,由于有多个sticker,所以要选择拼成相同的接近target的单词时取耗费最小的方案,

    按这个过程所拼成的单词不断靠近terget,最后到达target。因为target的长度已知,所以能拼成的靠近target的单词就都已知,所以就逐个扩展,如果不能从该靠近target的单词扩展就跳过。

    参考:https://leetcode.com/problems/stickers-to-spell-word/discuss/





    原创粉丝点击