1255. 得分最高的单词集合
乍一看觉得应该贪心,但是第二个样例就打脸了。 z z z作为贡献最大的字母, x x x z xxxz xxxz却不是得分最高的方案。再一看数据量,单词数小于14,那直接上DFS。
DFS
我们先统计每个字母的数量,然后模拟所有情况,即每个单词选与不选,一共 2 n 2^{n} 2n,用一个变量记录最高得分,每次遍历完所有单词的时候更新最高分,并返回。同时,每次剩余字母数不足以组成一个单词的时候也返回。
class Solution {
public:
int maxScoreWords(vector<string>& words, vector<char>& letters, vector<int>& score) {
int ans=0;
int len=words.size();
vector<int> ch(30,0);//记录26个字母的数量
for(auto c:letters){
ch[c-'a']++;
}
function<void(int, int)> dfs=[&](int index, int sum){
if(index>=len) {//所有单词都已走完,更新最高分
ans=max(ans,sum);
return;
}
dfs(index+1,sum);//不选当前单词,直接进入下一层
bool flag=true;
for(auto c:words[index]){//选中当前单词,减去这些字母,并加上该单词的分数
ch[c-'a']--;
sum+=score[c-'a'];
if(ch[c-'a']<0) flag=false;
}
if(flag) dfs(index+1,sum);//如果字母够用,进入下一层
for(auto c:words[index]){//字母不够用,加回用掉的字母
ch[c-'a']++;
}
};
dfs(0,0);
return ans;
}
};
状态压缩
看了题解发现有更容易理解的方法,就是用二进制去记录单词是否选中,顾名思义状态压缩。两个方法的解题思路是一样的,不同之处在于,状态压缩不需要恢复现场,只需要统计每一个方案所需字母数,与持有的字母数作比较即可知道方案的可行性。
class Solution {
public:
int maxScoreWords(vector<string>& words, vector<char>& letters, vector<int>& score) {
vector<int> count(30,0);
int ans=0;
int len=words.size();
for(auto letter:letters){
count[letter-'a']++;
}
for(int i=1;i<(1<<len);i++){
int sum=0;
vector<int> now_count(30,0);
for(int j=0;j<len;j++){
if(i&(1<<j)){
for(auto c:words[j]){
now_count[c-'a']++;
}
}
}
bool flag=true;
for(int j=0;j<26;j++){
sum+=score[j]*now_count[j];
flag=flag&&(now_count[j]<=count[j]);
}
if(flag){
ans=max(ans,sum);
}
}
return ans;
}
};