LC691. 贴纸拼词
我们有 n 种不同的贴纸。每个贴纸上都有一个小写的英文单词。
您想要拼写出给定的字符串 target ,方法是从收集的贴纸中切割单个字母并重新排列它们。如果你愿意,你可以多次使用每个贴纸,每个贴纸的数量是无限的。
返回你需要拼出 target 的最小贴纸数量。如果任务不可能,则返回 -1 。
注意:在所有的测试用例中,所有的单词都是从 1000 个最常见的美国英语单词中随机选择的,并且 target 被选择为两个随机单词的连接。
输入: stickers = ["with","example","science"], target = "thehat"
输出:3
解释:
我们可以使用 2 个 "with" 贴纸,和 1 个 "example" 贴纸。
把贴纸上的字母剪下来并重新排列后,就可以形成目标 “thehat“ 了。
此外,这是形成目标字符串所需的最小贴纸数量。
思路:记忆化递归
- 用二进制来表示单词中每个字母是否出现,设置数组 v[1 << m] 来表示当前字母出现的情况下最少需要多少张贴纸;
- 要求 v[i],就需要遍历每个贴纸,从每个贴纸中找到能抵消的字母,然后将抵消后剩下的字母所需的最少张贴纸 + 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class Solution {
public:
int minStickers(vector<string>& stickers, string target) {
int m = target.size();
vector<int> dp (1 << m, -1);
dp[0] = 0;
function<int(int)> helper = [&](int mask) {
if (dp[mask] != -1) {
return dp[mask];
}
dp[mask] = m + 1;
for (auto& sticker : stickers) { // 遍历每个贴纸
int left = mask;
vector<int> cnt(26);
for (char& c : sticker) { // 贴纸中有的字母
cnt[c - 'a'] ++;
}
for (int i = 0; i < m; i++) {
if ((mask >> i & 1) && cnt[target[i] - 'a'] > 0) {
cnt[target[i] - 'a']--;
left ^= 1 << i; // left 表示被抵消掉后剩下的字母
}
}
if (left < mask) {
dp[mask] = min(dp[mask], helper(left) + 1);
}
}
return dp[mask];
};
int res = helper((1 << m) - 1);
return res > m ? -1 : res;
}
};
|