贴纸拼词(DP解法)
感觉这题题解太少了,DP解法在CSDN也没找到,所以在自己才“一知半解”时,就想写篇DP解法,为大家提供思路的同时,也希望能得到大家的指点。
题目
我们给出了 N 种不同类型的贴纸。每个贴纸上都有一个小写的英文单词。
你希望从自己的贴纸集合中裁剪单个字母并重新排列它们,从而拼写出给定的目标字符串 target。
如果你愿意的话,你可以不止一次地使用每一张贴纸,而且每一张贴纸的数量都是无限的。
拼出目标 target 所需的最小贴纸数量是多少?如果任务不可能,则返回 -1。
示例1:
输入:
["with", "example", "science"], "thehat"
输出:
3
解释:
我们可以使用 2 个 "with" 贴纸,和 1 个 "example" 贴纸。
把贴纸上的字母剪下来并重新排列后,就可以形成目标 “thehat“ 了。
此外,这是形成目标字符串所需的最小贴纸数量。
DP思路:
假设
target="hat"
sticker1="haha"
sticker2="tata"
总共有1<<target.size()种状态,从开始一张没贴的000到最后全部贴好的111,
第一次:
000->我们可以用sticker1截取a,h用来拼,即110
也可以用sticker2截取t,a拼,即011
所以状态110和011所花贴纸均为1:
011->1
110->1
其余地方均为-1.
第二次:
状态向后遍历,遇到-1说明这些地方没有可用的贴纸直接跳过,直到遇到不为-1.
011->此处不为-1,继续截取贴纸拼写,发现可以从haha截取h拼,所以
111->2(tata+haha).
大致过程就是这样,接下来可以享用代码,加深理解:
class Solution {
public:
int minStickers(vector<string>& stickers, string target) {
int m=target.size();
vector<int>dp(1<<m,-1);
dp[0]=0;//长度为0无需贴纸.
for(int State=0;State<(1<<m);State++){
if(dp[State]==-1)//无可用贴纸直接跳过
continue;
for(string sticker:stickers){
int initialState=State;//在当前状态下往最终状态继续拼
for(char ch:sticker){//将贴纸的字符截取
for(int i=0;i<m;i++){
if((initialState>>i)&1==1)//之前已经贴好,则跳过
continue;
if(ch==target[i]){//否则就将当前字符取用,换下一个字符
initialState|=(1<<i);
break;
}
}
}
if(dp[initialState]==-1||dp[initialState]>dp[State]+1)//自己思考思考
dp[initialState]=dp[State]+1;
}
}
return dp[(1<<m)-1];
}
};