1255. 得分最高的单词集合

发布时间 2023-04-08 18:16:16作者: lxy_cn

题目链接:1255. 得分最高的单词集合

方法:暴力回溯

解题思路

观察可以发现,本题的数据量范围较小,使用暴力回溯不超过\(2^1\)\(^4\)次,需要注意的有,当选择一个单词时,必须保证当前提供的字符集合中剩余字符能够组成该单词\(check()\),选择以后将字符集合中对应字符数量减少\(destroy()\),回溯回来之后将其恢复\(resume()\)

做题过程中的问题

  • 当想使用记忆化搜索进行优化时,发现由于选择当前单词时需要进行判定,并减小字符集合中的字符数量,会导致 \(cache\) 数组的更新不准确;因为子问题与当前问题两者之间不是独立关系,子问题的选取会影响当前单词是否能选。
  • 不要在开始对 \(words\) 数组中的单词分数进行预处理,因为 \(words\) 中出现的单词可以重复,那么可能最终求得分数是重复单词分数之和。

代码

class Solution {
public:
    unordered_map<char, int> cnt; // 统计letters中字符出现的数量
    bool check(string word) { // 检查单词word是否能被组成
        unordered_map<char, int> temp_cnt;
        for (auto &c : word) temp_cnt[c] ++ ;
        for (auto &i : temp_cnt) {
            if (cnt[i.first] < i.second) return false;
        }
        return true;
    }
    void destroy(string word) { // 当选择某个word时,要减小对应字母的数量
        for (auto &c : word) cnt[c] -- ;
    }
    void resume(string word) { // 回溯时,恢复现场
        for (auto &c : word) cnt[c] ++ ;
    }
    int dfs(vector<string>& words, vector<int>& score, int idx) {
        if (idx < 0) return 0;
        int temp1 = 0;
        if (check(words[idx])) { // 选择当前word
            destroy(words[idx]);
            int wordScore = 0; // 计算当前word的得分
            for (auto &c : words[idx]) wordScore += score[c - 'a'];
            temp1 = dfs(words, score, idx - 1) + wordScore;
            resume(words[idx]);
        }
        return max(temp1, dfs(words, score, idx - 1)); // 在选择当前word 和 不选择两种情况中取最大值
    }
    int maxScoreWords(vector<string>& words, vector<char>& letters, vector<int>& score) {
        for (auto &c : letters) cnt[c] ++ ;
        return dfs(words, score, words.size() - 1);
    }
};

复杂度分析

时间复杂度:\(O(m + L * 2^n)\)\(m\)\(letters\) 数组长度,\(L\)\(words[i]\) 的长度,\(n\)\(words\) 数组长度;
空间复杂度:\(O(\Sigma)\)\(\Sigma\)为所有字符集合的大小,本题限定为小写字母,故\(\Sigma = 26\)