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.

这道题给了我们N个贴片,每个贴片上有一个小写字母的单词,给了我们一个目标单词target,让我们通过剪下贴片单词上的字母来拼出目标值,每个贴片都有无数个,问我们最少用几个贴片能拼出目标值target,如果不能拼出来的话,就返回-1。这道题博主最开始尝试用贪婪算法,结果发现不行,有网友留言提示说是多重背包问题,然后去论坛上看大神们的解法,果然都是用DP做的,之前曾有网友推荐过一个“背包九讲”的帖子,大概扫过几眼,真是叼到飞起啊,博主希望有时间也能总结一下。先来看这道题吧,既然是用DP来做,那么就需要用dp数组了,我们使用一个一维的dp数组,其中dp[i]表示组成第i个子集合所需要的最少的sticker的个数,注意这里是子集合,而不是子串。长度为n的字符串共有2的n次方个子集合,比如字符串"ab",就有4个子集合,分别是 "", "a", "b", "ab"。字符串"abc"就有8个子集合,如果我们用0到7来分别对应其子集合,就有:

     abc   subset
""
0 c
b
0 bc
1 a
ac
1 bb
abc

我们可以看到0到7的二进制数的每一位上的0和1就相当于mask,是1的话就选上该位对应的字母,000就表示都不选,就是空集,111就表示都选,就是abc,那么只要我们将所有子集合的dp值都算出来,最后返回dp数组的最后一个位置上的数字,就是和目标子串相等的子集合啦。我们以下面这个简单的例子来讲解:

stickers = ["ab", "bc", "ac"], target = "abc"

之前说了abc的共有8个子集合,所以dp数组的长度为8,除了dp[0]初始化为0之外,其余的都初始化为INT_MAX,然后我们开始逐个更新dp数组的值,我们的目标是从sticker中取出字符,来拼出子集合,所以如果当前遍历到的dp值仍为INT_MAX的话,说明该子集合无法被拼出来,自然我们也无法再其基础上去拼别打子集合了,直接跳过。否则我们就来遍历所有的sticker,让变量cur等于i,说明此时是在第i个子集合的基础上去reach其他的子集合,我们遍历当前sticker的每一个字母,对于sticker的每个字母,我们都扫描一遍target的所有字符,如果target里有这个字符,且该字符未出现在当前cur位置的子集合中,则将该字符加入子集合中。什么意思呢,比如当前我们的cur是3,二进制为011,对应的子集合是"bc",此时如果我们遍历到的sticker是"ab",那么遇到"a"时,我们知道target中是有"a"的,然后我们看"bc"中包不包括"a",判断方法是看 (cur >> k) & 1 是否为0,这可以乍看上去不太好理解,其实不难想,因为我们的子集合是跟二进制对应的,"bc"就对应着011,第一个0就表示"a"缺失,所以我们想看哪个字符,就提取出该字符对应的二进制位,提取方法就是 cur >> k,表示cur向右移动k位,懂位操作Bit Manipulation的童鞋应该不难理解,提出出来的值与上1就知道该位是0还是1了,如果是0,表示缺失,那么把该位变为1,通过 cur |= 1 << k来实现,那么此时我们的cur就变位7,二进制为111,对应的子集合是"abc",更新此时的dp[cur]为 dp[cur] 和 dp[i] + 1 中的较大值即可,最后循环结束后,如果"abc"对应的dp值还是INT_MAX,就返回-1,否则返回其dp值,参见代码如下:

解法一:

class Solution {
public:
int minStickers(vector<string>& stickers, string target) {
int n = target.size(), m = << n;
vector<int> dp(m, INT_MAX);
dp[] = ;
for (int i = ; i < m; ++i) {
if (dp[i] == INT_MAX) continue;
for (string &sticker : stickers) {
int cur = i;
for (char c : sticker) {
for (int k = ; k < n; ++k) {
if (target[k] == c && !((cur >> k) & )) {
cur |= << k;
break;
}
}
}
dp[cur] = min(dp[cur], dp[i] + );
}
}
return dp[m - ] == INT_MAX ? - : dp[m - ];
}
};

下面这种解法是带记忆数组memo的递归解法,可以看作是DP解法的递归形式,核心思想都一样。只不过dp数组换成了哈希Map,建立子集合跟最小使用的sticker的个数之间的映射,初始化空集为0,我们首先统计每个sticker的各个字母出现的频率,放到对应的二维数组freq中,然后就是调用递归函数。在递归函数中,首先判断,如果target已经在memo中,直接返回其值。否则我们开始计算,首先统计出此时的target字符串的各个字母出现次数,然后我们遍历统计所有sticker中各个字母出现次数的数组freq,如果target字符串的第一个字母不在当前sticker中,我们直接跳过,注意递归函数中的target字符串不是原始的字符串,我们心间一个临时字符串t,然后我们遍历target字符串中存在的字符,如果target中的某字符存在的个数多于sticker中对应的字符,那么将多余的部分存在字符串t中,表示当前sticker无法拼出的字符,交给下一个递归函数来处理,我们看再次调用递归函数的结果ans,如果不为-1,说明可以拼出剩余的那些字符,那么此时我们的res更新为ans+1,循环退出后,此时我们的res就应该是组成当前递归函数中的target串的最少贴片数,更新dp[target]值,如果res是INT_MAX,说明无法拼出,更新为-1,否则更新为res,参见代码如下:

解法二:

class Solution {
public:
int minStickers(vector<string>& stickers, string target) {
int N = stickers.size();
vector<vector<int>> freq(N, vector<int>(, ));
unordered_map<string, int> memo;
memo[""] = ;
for (int i = ; i < N; ++i) {
for (char c : stickers[i]) ++freq[i][c - 'a'];
}
return helper(freq, target, memo);
}
int helper(vector<vector<int>>& freq, string target, unordered_map<string, int>& memo) {
if (memo.count(target)) return memo[target];
int res = INT_MAX, N = freq.size();
vector<int> cnt(, );
for (char c : target) ++cnt[c - 'a'];
for (int i = ; i < N; ++i) {
if (freq[i][target[] - 'a'] == ) continue;
string t = "";
for (int j = ; j < ; ++j) {
if (cnt[j] - freq[i][j] > ) t += string(cnt[j] - freq[i][j], 'a' + j);
}
int ans = helper(freq, t, memo);
if (ans != -) res = min(res, ans + );
}
memo[target] = (res == INT_MAX) ? - : res;
return memo[target];
}
};

类似题目:

Ransom Note

参考资料:

https://leetcode.com/problems/stickers-to-spell-word/discuss/108333/Rewrite-of-contest-winner's-solution

https://leetcode.com/problems/stickers-to-spell-word/discuss/108318/C++JavaPython-DP-+-Memoization-with-optimization-29-ms-(C++)

https://leetcode.com/problems/stickers-to-spell-word/discuss/108334/Explaining-StefanPochmann's-Rewrite-of-contest-winner's-solution-and-+java

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Stickers to Spell Word 贴片拼单词的更多相关文章

  1. [Swift]LeetCode691. 贴纸拼词 | Stickers to Spell Word

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

  2. LeetCode 691. Stickers to Spell Word

    原题链接在这里:https://leetcode.com/problems/stickers-to-spell-word/ 题目: We are given N different types of ...

  3. LeetCode691. Stickers to Spell Word

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

  4. 691. Stickers to Spell Word

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

  5. [LeetCode] Length of Last Word 求末尾单词的长度

    Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the l ...

  6. [Leetcode] Length of last word 最后一个单词的长度

    Given a string s consists of upper/lower-case alphabets and empty space characters' ', return the le ...

  7. [LeetCode] Valid Word Square 验证单词平方

    Given a sequence of words, check whether it forms a valid word square. A sequence of words forms a v ...

  8. [LeetCode] Valid Word Abbreviation 验证单词缩写

    Given a non-empty string s and an abbreviation abbr, return whether the string matches with the give ...

  9. [LeetCode] Maximum Product of Word Lengths 单词长度的最大积

    Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the tw ...

随机推荐

  1. 爬虫(requests)

    requests库包含两个对象:Response和Requests  Response对象属性:    r.status_code    HTTP请求的返回状态,200表示成功 r.text     ...

  2. 使用MVC5+Entity Framework6的Code First模式创建数据库并实现增删改查功能

    此处采用VS2017+SqlServer数据库 一.创建项目并引用dll: 1.创建一个MVC项目 2.采用Nuget安装EF6.1.3 二.创建Model 在models文件夹中,建立相应的mode ...

  3. Java Collections API和泛型

    Java Collections API和泛型 数据结构和算法 学会一门编程语言,你可以写出一些可以工作的代码用计算机来解决一些问题,然而想要优雅而高效的解决问题,就要学习数据结构和算法了.当然对数据 ...

  4. 2017北京国庆刷题Day4 morning

    期望得分:0+40+30=70 实际得分:0+10+10=20 题目修改:只能由0变1,只能用一次操作 大模拟 #include<cstdio> #include<cstring&g ...

  5. hdu 3642 Get The Treasury

    Get The Treasury http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Othe ...

  6. python使用tesseract-ocr完成验证码识别(安装部分)

    一.tesseract-ocr安装 Ubuntu版本: 1.tesseract-ocr安装 sudo apt-get install tesseract-ocr 2.pytesseract安装 sud ...

  7. WebApi一个控制器中定义多个Get方法。

    问题:怎样解决一个ApiController中定义多个Get方法或者Post方法? 答:要想实现一个ApiController中定义多个Get方法或者Post方法,则需要在WebApiConfig类中 ...

  8. IDEA插件和快捷设置

    前言 IDEA全名Intellij IDEA,是Java开发的集成环境,它有两个版本,专业版(Ultimate)和社区版(Community),专业版需要注册,而社区版不用注册,同时需要注意的是社区版 ...

  9. 基于 Java NIO 实现简单的 HTTP 服务器

    1.简介 本文是上一篇文章实践篇,在上一篇文章中,我分析了选择器 Selector 的原理.本篇文章,我们来说说 Selector 的应用,如标题所示,这里我基于 Java NIO 实现了一个简单的 ...

  10. 使用location.href跳转页面在火狐浏览器中报错404

    HTML文件中引入外部js文件,在该js文件里用window.location.href跳转相对路径下的html地址,火狐浏览器会报错404,而谷歌浏览器却显示正常·,分析了一下原因:在识别相对路径时 ...