Leetcode 691. 贴纸拼词
题目
我们给出了 N 种不同类型的贴纸。每个贴纸上都有一个小写的英文单词。
你希望从自己的贴纸集合中裁剪单个字母并重新排列它们,从而拼写出给定的目标字符串 target。
如果你愿意的话,你可以不止一次地使用每一张贴纸,而且每一张贴纸的数量都是无限的。
拼出目标 target 所需的最小贴纸数量是多少?如果任务不可能,则返回 -1。
测试样例
示例 1:
输入:
["with", "example", "science"], "thehat"
输出:
3
解释:
我们可以使用 2 个 "with" 贴纸,和 1 个 "example" 贴纸。
把贴纸上的字母剪下来并重新排列后,就可以形成目标 “thehat“ 了。
此外,这是形成目标字符串所需的最小贴纸数量。
示例 2:
输入:
["notice", "possible"], "basicbasic"
输出:
-1
解释:
我们不能通过剪切给定贴纸的字母来形成目标“basicbasic”。
提示:
- stickers 长度范围是 [1, 50]。
- stickers 由小写英文单词组成(不带撇号)。
- target 的长度在 [1, 15]范围内,由小写字母组成。
- 在所有的测试案例中,所有的单词都是从 1000 个最常见的美国英语单词中随机选取的,目标是两个随机单词的串联。
- 时间限制可能比平时更具挑战性。预计 50 个贴纸的测试案例平均可在35ms内解决。
题解
状态压缩+动态规划
令dp[state]表示完成state状态的字母拼写需要的最小贴纸数,其中state是具有target.length位的数,若第i位(低位)为1,则表明target[i]完成拼写。
我们便利每个状态,对于每个状态,我们选取贴纸对其进行改变,我们需要将这个贴纸的单词尽可能的用于target上。因此,如果changeState & (1<<j),其中changeState是当前的一个状态,j表明target[j]。
转移方程的具有解释见代码
代码
int minStickers(vector<string>& stickers, string target) {
int n = stickers.size(), len = target.length();
vector<vector<int>> time(n,vector<int>(26,0));
for(int i=0; i<n; i++){ //统计每个单词能使用的字母数
for(int j=0; j<stickers[i].length(); j++)
time[i][stickers[i][j]-'a']++;
}
vector<int> dp(1<<len,-1);
dp[0] = 0;
for(int state=0; state<(1<<len); state++){ //遍历每个状态,对该状态进行改变
if(dp[state]==-1) continue; //当前状态不能实现
for(int i=0; i<n; i++){ //选取第i个贴纸,需要尽可能的使用其中的字母
int changeState = state;
vector<int> now = time[i]; //记录可以使用的字母
for(int j=0; j<len; j++){ //判断target[j]是否已贴上,未贴上则判断当前贴纸是否可能完成该字母
if(changeState & (1<<j)) continue; //该位置已经贴上了
else if(now[target[len-j-1]-'a'] != 0){ //可以用来贴当前字母
changeState |= (1<<j); //更新状态
now[target[len-j-1]-'a']--; //减少可用字母数
}
}
if(dp[changeState]==-1 || dp[changeState]>dp[state]+1) //当前完成changeState状态所用贴纸数最少
dp[changeState] = dp[state]+1;
}
}
return dp[(1<<len)-1];
}
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/stickers-to-spell-word
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。