讨论/题目交流/🏆 第 184 场力扣周赛/
🏆 第 184 场力扣周赛

欢迎小伙伴们在这里交流分享你的参赛心得以及体验。【前往竞赛

image.png

3 分 - 数组中的字符串匹配
4 分 - 查询带键的排列
5 分 - HTML 实体解析器
7 分 - 给 N x 3 网格图涂色的方案数

展开讨论

LeetCode 第 184 场周赛解题报告 (C++)

5380. 数组中的字符串匹配

O(n^2) 暴力枚举,具体思路见注释。介绍一下 string 的 find 函数。

  • std::string::find 函数。接收一个字符串,返回该字符串第一次出现的位置。
  • 如果没有找到,返回 std::string::npos。
class Solution {
public:
    vector<string> stringMatching(vector<string>& words) {
        vector<string> res;
        //枚举每个字符串作为子串。
        for(int i = 0; i < words.size(); i++) {
            //对于每个子串,枚举所有字符串作为其母串。
            for(int j = 0; j < words.size(); j++) {
                //母串和子串不能是同一个字符串,所以需要 i != j
                //检查words[i] 是否是 words[j] 的子串。
                if(i != j && words[j].find(words[i]) != std::string::npos) {
                    res.push_back(words[i]);
                    break;
                }
            }
        }
        return res;
    }
};

5381. 查询带键的排列

O(m*q) 暴力枚举。使用链表模拟即可,具体思路见注释。简单介绍下 std::list 的 insert 和 erase 函数。

  • std::list::insert 可以接受一个std::list::iterator iter和一个待插入元素 p。该函数会将 p 插入到 iter 之前。
  • std::list::erase 可以接受一个std::list::iterator iter。该函数会删除 iter 指向的结点。
class Solution {
public:
    vector<int> processQueries(vector<int>& queries, int m) {
        list<int> dataList;
        //初始化排列。
        for(int i = 1; i <= m; i++) {
            dataList.push_back(i);
        }
        vector<int> res;
        //处理每个请求
        for(auto v : queries) {
            auto it = dataList.begin();
            int ans = 0;
            //O(m)的查找当前询问的位置。
            for(auto it = dataList.begin(); it != dataList.end(); ++it, ++ans) {
                // 找到了
                if(*it == v) { 
                    res.push_back(ans); //记录答案
                    //在链表的头部插入该元素。
                    dataList.insert(dataList.begin(), *it);
                    //删除旧的元素。
                    dataList.erase(it);
                    break;
                }
            }
        }
        return res;
    }
};

5382. HTML 实体解析器

准备数据容器

使用 unordered_map<string, string> 记录HTML的特殊字符和它们对应的字符实体。
使用一个新的string记录答案。

遍历 text

从头遍历 text,如果以 text[i] 为起点的字符串在 unordered_map 中出现了,那么将对应的字符实体放入 string 中。否则直接将 text[i] 放入string中。具体细节见注释。

class Solution {
public:
    string entityParser(string text) {
        string res;
        unordered_map<string, string> dict; //将特殊字符和字符实体放入 dict 中。
        dict["&quot;"] = "\"";
        dict["&apos;"] = "'";
        dict["&amp;"] = "&";
        dict["&gt;"] = ">"; 
        dict["&lt;"] = "<";
        dict["&frasl;"] = "/";
        
        for(int i = 0, n = text.size(); i < n; ) {//遍历text
            bool match = false;
            //判断以text[i]为起点的字符串是否是特殊字符
            for(auto p = dict.cbegin(); p != dict.cend(); p++) {
                //匹配上了
                if(text.substr(i, p->first.size()) == p->first) {
                    res += p->second;
                    i += p->first.size(); //i 后移 p->first.size()
                    match = true; //标记已匹配
                    break;
                } 
            }
            if(match) {
                continue;
            }
            res += text[i++]; //未匹配上,将 text[i] 放入 res。i 后移一位。
        }
        return res;
    }
};

5383. 给 N x 3 网格图涂色的方案数

解题套路:状态压缩 + 递推。

状态压缩

用一个数字表示集合的一种状态。
比如在本题中,三个并列的格子可以看作是一个集合。我们用数字表示这个集合的状态,也就是染色方案。
具体的表示方法如下:
因为只有三种颜色,所以可以用三进制数字来表示染色方案。用 0,1,2 分别代表红黄绿。因为每个集合只有三个格子,所以只需要用三位数字
比如21 = 0 + 31 + 92,代表第一个格子为红色,第二个格子为黄色,第三个格子为绿色。
我们可以把这个数字称为状态码

关于状态码的取值范围

当三个格子都染成红色时,状态码取得最小值即 0 + 03 + 09 = 0。
当三个格子都染成绿色时,状态码取得最大值即 2 + 23 + 29 = 26。
所以状态码的取值范围为[0,26]。

递归

设 dp[n][sc] 为将前 n 层都染上颜色,且第n层的染色为 sc。sc 即上述的状态码。
当 n 等于 1时,dp[n][sc] 很好计算,即判断sc代表的染色方案是否合法即可。
如果合法 dp[1][sc] = 1,否则 dp[1][sc] = 0。

当 n > 1 时,dp[n][sc] 由 dp[n-1][psc] 推导得出。sc 为 第n行的染色方案,psc为 n-1行的染色方案。递推式子如下:
dp[n][sc] += dp[n-1][psc] (0 <= psc < 27)。

class Solution {
public:
    const int mod = 1000000007;
    int dp[5001][27] = {0};
    //根据每个格子的颜色获取状态码
    int sc(int i, int j, int k) {
        return i + j*3 + k *9;
    }
    //根据状态码获取每个格子的颜色
    void cs(int statusCode, int &a, int &b, int &c) {
        a = statusCode % 3;
        b = (statusCode - a)%9/3;
        c = statusCode/9;
    }
    
    int numOfWays(int n) {
        初始化 n = 1 的方案数。
        for(int i = 0; i <= 2; i++) {
            for(int j = 0; j <= 2; j++) {
                for(int k = 0; k <= 2; k++) {
                    if(i != j && j != k) {
                        dp[1][sc(i,j,k)] = 1;
                    }
                }
            }
        }
        //枚举行数
        for(int level = 2; level <= n; level++) {
            //枚举前一行的染色方案
            for(int pre = 0; pre < 27; ++pre) {
                int a, b, c;
                cs(pre, a, b, c);
                if(a == b || b == c) {
                    continue;
                }
                //枚举当前行的染色方案
                for(int i = 0; i <= 2; i++) {
                    if(i == a) { continue; } //检查是否冲突
                    for(int j = 0; j <= 2; j++) {
                        if(i == j || j == b) { continue; } //检查是否冲突
                        for(int k = 0; k <= 2; k++) {
                            if(j == k || k == c) { continue; } //检查是否冲突
                            (dp[level][sc(i,j,k)] += dp[level-1][pre]) %= mod; //当前染色方案可行,累加方案数。
                        }
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 0; i < 27; i++) {
            (ans += dp[n][i]) %= mod; //计算总的方案数。
        }
        return ans;
    }
};
3
展开全部 46 讨论