当前位置: 首页 > article >正文

leetcode解题思路分析(一百六十一)1394 - 1400 题

  1. 找出数组中的幸运数
    在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。给你一个整数数组 arr,请你从中找出并返回一个幸运数。如果数组中存在多个幸运数,只需返回 最大 的那个。如果数组中不含幸运数,则返回 -1 。

一个哈希表搞定

class Solution {
public:
    int findLucky(vector<int>& arr) {
        unordered_map<int, int> cnt_map;
        for (auto n : arr) {
            cnt_map[n]++;
        }

        int ret = -1;
        for (auto it : cnt_map) {
            if (it.first == it.second) {
                ret = max(ret, it.first);
            }
        }
        return ret;
    }
};
  1. 统计作战单位数
    n 名士兵站成一排。每个士兵都有一个 独一无二 的评分 rating 。
    从中选出 3 个士兵组成一个作战单位,规则如下:
    从队伍中选出下标分别为 i、j、k 的 3 名士兵,他们的评分分别为 rating[i]、rating[j]、rating[k]
    作战单位需满足: rating[i] < rating[j] < rating[k] 或者 rating[i] > rating[j] > rating[k] ,其中 0 <= i < j < k < n
    请你返回按上述条件组建的作战单位的方案数。

最简单的是三层暴力查找,但是其实可以遍历中间值,然后左右各查一次再相乘即可。当然,还有更好的做法:提前遍历一遍,记录当前位置i前面有多少个比i大,多少个比i小,利用前缀和的特性实现。

class Solution {
public:
    int numTeams(vector<int>& rating) {
        int n = rating.size();
        int ret = 0;

        for (int i = 1; i < n - 1; ++i) {
            auto inc_less = 0, inc_more = 0;
            auto dec_less = 0, dec_more = 0;

            for (int j = 0; j < i; ++j) {
                if (rating[j] < rating[i]) 
                    inc_less++;
                if (rating[j] > rating[i])
                    dec_more++;
            }

            for (int k = i + 1; k < n; ++k) {
                if (rating[k] > rating[i])
                    inc_more++;
                if (rating[k] < rating[i])
                    dec_less++;
            }

            ret += inc_less * inc_more + dec_less * dec_more;
        }

        return ret;
    }
};
  1. 设计地铁系统
    地铁系统跟踪不同车站之间的乘客出行时间,并使用这一数据来计算从一站到另一站的平均时间。实现 UndergroundSystem 类。

很简单的设计题

class UndergroundSystem {
public:
    using Start = pair <string, int>;
    using StartEnd = pair <string, string>;
    using SumAndAmount = pair <int, int>;

    struct StartEndHash {
        int operator() (const StartEnd& x) const{
            return hash <string> () (x.first + x.second);
        }
    };

    unordered_map <int, Start> startInfo;
    unordered_map <StartEnd, SumAndAmount, StartEndHash> table;
    
    UndergroundSystem() {}
    
    void checkIn(int id, string stationName, int t) {
        startInfo[id] = {stationName, t};
    }
    
    void checkOut(int id, string stationName, int t) {
        auto startTime = startInfo[id].second;
        table[{startInfo[id].first, stationName}].first += t - startTime;
        table[{startInfo[id].first, stationName}].second++;
    }
    
    double getAverageTime(string startStation, string endStation) {
        auto sum = table[{startStation, endStation}].first;
        auto amount = table[{startStation, endStation}].second;
        return double(sum) / amount;
    }
};

  1. 找到所有好字符串
    给你两个长度为 n 的字符串 s1 和 s2 ,以及一个字符串 evil 。请你返回 好字符串 的数目。好字符串 的定义为:它的长度为 n ,字典序大于等于 s1 ,字典序小于等于 s2 ,且不包含 evil 为子字符串。由于答案可能很大,请你返回答案对 10^9 + 7 取余的结果。

数位DP的经典使用场景:给定下界 l 和上界 r,求 [l,r] 之间满足某一要求的元素个数。结合KMP,可以解题。

class Solution {
private:
    // 这是用来帮助计算关于 stats 的转移函数的 fail 数组
    vector<int> fail;
    // 这是存储动态规划结果的数组
    // 维度从左到右分别为 pos, stats, bound
    int f[500][50][4];
    // 这是存储转移函数结果的数组
    // 两个维度分别为 stats 和 d
    int trans[50][26];
    static constexpr int mod = 1000000007;
    string s1, s2, evil;

public:
    // 这是计算关于 stats 的转移函数
    // 输入为 stats 和 d
    // 输出为转移的结果 g_s(stats, d)
    int getTrans(int stats, char ch) {
        // 如果之前计算过该转移函数就直接返回结果
        if (trans[stats][ch - 97] != -1) {
            return trans[stats][ch - 97];
        }
        // 这是 KMP 算法的一部分
        // 把 evil 看作「模式串」,stats 看作「主串」匹配到的位置
        while (stats && evil[stats] != ch) {
            stats = fail[stats - 1];
        }
        // 存储结果并返回
        return trans[stats][ch - 97] = (evil[stats] == ch ? stats + 1 : 0);
    }

    // 这是用来进行记忆化搜索的函数
    // 输入为 pos, stats 和 bound
    // 输出为 f[pos][stats][bound]
    int dfs(int pos, int stats, int bound) {
        // 如果匹配到了 evil 的末尾
        // 说明字符串不满足要求了
        // 返回 0
        if (stats == evil.size()) {
            return 0;
        }
        // 如果匹配到了上下界的末尾
        // 说明找到了一个满足要求的字符串
        // 返回 1
        if (pos == s1.size()) {
            return 1;
        }
        // 记忆化搜索的关键
        // 如果之前计算过该状态就直接返回结果
        if (f[pos][stats][bound] != -1) {
            return f[pos][stats][bound];
        }
        
        // 将当前状态初始化
        // 因为未初始化的状态值为 -1
        f[pos][stats][bound] = 0;
        // 计算第 pos 位可枚举的字符下界
        char l = (bound & 1 ? s1[pos] : 'a');
        // 计算第 pos 位可枚举的字符上界
        char r = (bound & 2 ? s2[pos] : 'z');
        for (char ch = l; ch <= r; ++ch) {
            int nxt_stats = getTrans(stats, ch);
            // 这里写得较为复杂
            // 本质上就是关于 bound 的转移函数
            // 可以根据自己的理解编写
            int nxt_bound = (bound & 1 ? ch == s1[pos] : 0) ^ (bound & 2 ? (ch == s2[pos]) << 1 : 0);
            f[pos][stats][bound] += dfs(pos + 1, nxt_stats, nxt_bound);
            f[pos][stats][bound] %= mod;
        }
        return f[pos][stats][bound];
    }

    int findGoodStrings(int n, string _s1, string _s2, string _evil) {
        s1 = _s1;
        s2 = _s2;
        evil = _evil;

        int m = evil.size();
        fail.resize(m);
        // 这是 KMP 算法的一部分
        // 把「evil」看作模式串,得到它的 fail[] 数组
        for (int i = 1; i < m; ++i) {
            int j = fail[i - 1];
            while (j && evil[j] != evil[i]) {
                j = fail[j - 1];
            }
            if (evil[j] == evil[i]) {
                fail[i] = j + 1;
            }
        }
        // 将未搜索过的动态规划状态置为 -1
        memset(f, -1, sizeof(f));
        // 将未计算过的转移函数置为 -1
        memset(trans, -1, sizeof(trans));
        // 答案即为 f[0][0][3]
        return dfs(0, 0, 3);
    }
};


  1. 统计最大组的数目
    给你一个整数 n 。请你先求出从 1 到 n 的每个整数 10 进制表示下的数位和(每一位上的数字相加),然后把数位和相等的数字放到同一个组中。请你统计每个组中的数字数目,并返回数字数目并列最多的组有多少个。

哈希表存一下然后遍历一遍读取即可。

class Solution {
public:
    int countLargestGroup(int n) {
        std::unordered_map<int, int> bit_sum_map;
        int max_val = 0;

        for (int num = 1; num <= n; ++num) {
            int sum = 0, n = num;
            while (n) {
                sum += n % 10;
                n = n / 10;
            }
            if (bit_sum_map.find(sum) != bit_sum_map.end()) 
                bit_sum_map[sum]++;
            else
                bit_sum_map[sum] = 1;
            max_val = max(max_val, bit_sum_map[sum]);
        }

        if (bit_sum_map.size() == 0) return 0;

        int ret = 0;
        for (auto iter : bit_sum_map) {
            if (max_val == iter.second)
                ret++;
        }

        return ret;
    }
};
  1. 构造 K 个回文字符串
    给你一个字符串 s 和一个整数 k 。请你用 s 字符串中 所有字符 构造 k 个非空 回文串 。如果你可以用 s 中所有字符构造 k 个回文字符串,那么请你返回 True ,否则返回 False 。

如果 s 中有 p 个字符出现了奇数次,q 个字符出现了偶数次,那么 s 最少可以构造的回文串个数就为 p,这是因为每一种出现了奇数次的字符都必须放在不同的回文串中。特别地,如果 p=0,那么最少构造的回文串个数为 1。而最大回文串个数则为字符种类

class Solution {
public:
    bool canConstruct(string s, int k) {
        // 右边界为字符串的长度
        int right = s.size();
        // 统计每个字符出现的次数
        int occ[26] = {0};
        for (char ch: s) {
            ++occ[ch - 'a'];
        }
        // 左边界为出现奇数次字符的个数
        int left = 0;
        for (int i = 0; i < 26; ++i) {
            if (occ[i] % 2 == 1) {
                ++left;
            }
        }
        // 注意没有出现奇数次的字符的特殊情况
        left = max(left, 1);
        return left <= k && k <= right;
    }
};



http://www.kler.cn/a/349436.html

相关文章:

  • ros service不走是为什么
  • STM32的独立看门狗定时器(IWDG)技术介绍
  • spring面试之2024
  • 【量化交易】聚宽安装
  • Linux虚拟化技术嬗变综述
  • MeterSphere接口自动化平台调试
  • 使用 Spring 框架构建 MVC 应用程序:初学者教程
  • 【三】【算法】P1007 独木桥,P1012 [NOIP1998 提高组] 拼数,P1019 [NOIP2000 提高组] 单词接龙
  • 元学习案例(学习如何学习)
  • vue3中如何给响应式对象
  • 微分几何-曲线论(向量函数)
  • 2024年【汽车驾驶员(技师)】考试题库及汽车驾驶员(技师)试题及解析
  • 集合框架05:List接口使用、List实现类、ArrayList使用
  • ListView的Items绑定和comboBox和CheckBox组合使用实现复选框的功能
  • C/C++语言基础--C++异常看这一篇就够了
  • 实景三维赋能自然资源精细化管理创新
  • 【翻译】自定义 Qt Designer 窗体
  • Oracle低代码平台apex介绍
  • 代码训练营 day28|LeetCode 39,LeetCode 40,LeetCode 131
  • Web Worker加载外部文件实践