10.16-10.25力扣计数刷题

计数相关知识:

1.加法原理

如果完成一件事有n类方法,第i类方法有mᵢ种方式,则总方法数为:总方法数 = m₁ + m₂ + ... + mₙ

2.乘法原理

如果完成一件事需要n个步骤,第i步有mᵢ种方式,则总方法数为:总方法数 = m₁ × m₂ × ... × mₙ

3.排列

从n个不同元素中取出m个元素按顺序排列:P(n, m) = n! / (n-m)!。示例:5个人选3个排成一排:P(5,3) = 5!/(5-3)! = 60

4.组合

从n个不同元素中取出m个元素不考虑顺序:C(n, m) = n! / (m! × (n-m)!)

【1】594. 最长和谐子序列

日期:10.16

1.题目链接:594. 最长和谐子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-harmonious-subsequence/description/?envType=problem-list-v2&envId=counting

2.题目类型:数组,哈希表,排序,滑动窗口

3.方法一:滑动窗口(一次题解)

先对数组进行排序,这样相同的数字会聚集在一起

使用滑动窗口技术找到满足条件的最长子数组

和谐子序列的条件:窗口内最大值 - 最小值 = 1

begin 指向第一个连续相同元素的子序列的第一个元素,end 指向相邻的第二个连续相同元素的子序列的末尾元素,如果满足二者的元素之差为 1,则当前的和谐子序列的长度即为两个子序列的长度之和,等于 end−begin+1。

关键代码:

cpp 复制代码
        sort(nums.begin(),nums.end());
        int sum=0;
        int begin=0;// 滑动窗口的起始位置
            for(int end=0;end<nums.size();end++){
            // 当窗口内最大值与最小值的差超过1时,收缩窗口
                while(nums[end]-nums[begin]>1){
                begin++;
            }
                if(nums[end]-nums[begin]==1){
                    sum=max(sum,end-begin+1);
            }
        }

4.方法二:哈希表(半解)

首先遍历一遍数组,得到哈希映射。随后遍历哈希映射,设当前遍历到的键值对为 (x,value),那么就查询 x+1 在哈希映射中对应的统计次数,就得到了 x 和 x+1 出现的次数,和谐子序列的长度等于 x 和 x+1 出现的次数之和。

关键代码:

cpp 复制代码
        for(int num:nums){
            cnt[num]++;
        }        
        //遍历哈希表,检查相邻数字
        for(auto [key, val]:cnt){
            if(cnt.count(key+1)){
                // 更新结果为当前数字和相邻数字频率和的最大值
                res=max(res,val+cnt[key+1]);
            }
        }

【2】811. 子域名访问计数

日期:10.17

1.题目链接:811. 子域名访问计数 - 力扣(LeetCode)https://leetcode.cn/problems/subdomain-visit-count/description/?envType=problem-list-v2&envId=counting

2.题目类型:数组,哈希表,字符串,计数

3.方法一:哈希表(半解)

为了获得每个子域名的计数配对域名,需要使用哈希表记录每个子域名的计数。遍历数组 cpdomains,对于每个计数配对域名,获得计数和完整域名,更新哈希表中的每个子域名的访问次数。

遍历数组 cpdomains 之后,遍历哈希表,对于哈希表中的每个键值对,关键字是子域名,值是计数,将计数和子域名拼接得到计数配对域名,添加到答案中。

substr(0, space) 从字符串 cpdomain 中提取子字符串

stoi() 是 "string to integer" 的缩写,将字符串转换为整数,会自动处理前导空格和符号

关键代码:

cpp 复制代码
        for(auto &&cpdomain:cpdomains){
            // 分离数字和域名
            int space=cpdomain.find(' ');//找到空格位置,分离数字部分和域名部分
            int count=stoi(cpdomain.substr(0, space));//将数字字符串转换为整数
            string domain=cpdomain.substr(space + 1);//获取域名字符串         
            // 统计完整域名
            counts[domain]+=count;          
            // 统计所有子域名
            for(int i=0;i<domain.size();i++){
                if(domain[i]=='.'){
                    string subdomain=domain.substr(i+1);
                    counts[subdomain]+=count;
                }
            }
        }        
        // 将统计结果转换为输出格式
        for(auto &&[subdomain,count]:counts){
            ans.emplace_back(to_string(count)+" "+subdomain);
        }

【3】819. 最常见的单词

日期:10.18

1.题目链接:819. 最常见的单词 - 力扣(LeetCode)https://leetcode.cn/problems/most-common-word/description/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,计数

3.方法一:哈希表+计数(半解)

需要使用哈希集合存储禁用单词列表中的单词。

遍历段落 paragraph,得到段落中的所有单词,并对每个单词计数,使用哈希表记录每个单词的计数。由于每个单词由连续的字母组成,因此当遇到一个非字母的字符且该字符的前一个字符是字母时,即为一个单词的结束,如果该单词不是禁用单词,则将该单词的计数加 1。如果段落的最后一个字符是字母,则当遍历结束时需要对段落中的最后一个单词判断是否为禁用单词,如果不是禁用单词则将次数加 1。

在遍历段落的过程中,对于每个单词都会更新计数,因此遍历结束之后即可得到最大计数,即出现次数最多的单词的出现次数。遍历段落之后,遍历哈希表,寻找出现次数等于最大计数的单词,该单词即为最常见的单词。

isalpha() 判断字符是否为字母

tolower() 将字符转换为小写,实现大小写不敏感

关键代码:

cpp 复制代码
         // 遍历段落,提取单词并统计频率
        for(int i=0;i<=length;i++){
            // 如果是字母字符,添加到当前单词(转换为小写)
            if(i<length&&isalpha(paragraph[i])){
                word.push_back(tolower(paragraph[i]));
            } 
            // 如果遇到非字母字符或到达结尾,且当前单词不为空
            else if(word.size()>0){
                // 如果单词不在禁用列表中
                if(!bannedSet.count(word)){
                    frequencies[word]++;  
                    maxFrequency=max(maxFrequency,frequencies[word]);  // 更新最大频率
                }
                word="";  // 重置当前单词
            }
        }

【4】884. 两句话中的不常见单词

日期:10.19

1.题目链接:884. 两句话中的不常见单词 - 力扣(LeetCode)https://leetcode.cn/problems/uncommon-words-from-two-sentences/description/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,计数

3.方法一:哈希表(官方题解)

找出在两个句子中一共只出现一次的单词。

因此我们可以使用一个哈希映射统计两个句子中单词出现的次数。对于哈希映射中的每个键值对,键表示一个单词,值表示该单词出现的次数。在统计完成后,我们再对哈希映射进行一次遍历,把所有值为 1 的键放入答案中即可。

stringstream 自动按空格分割单词

move(word) 使用移动语义提高性能

关键代码:

cpp 复制代码
        // 定义lambda函数用于分割句子并统计单词频率
        auto insert=[&](const string& s){
            stringstream ss(s);  // 创建字符串流
            string word;
            while(ss>>word) {  // 自动按空格分割单词
                ++freq[move(word)];  // 移动语义,避免不必要的拷贝
            }
        };
        insert(s1);
        insert(s2);
        // 收集只出现一次的单词
        vector<string> ans;
        for(const auto& [word, occ]:freq){
            if(occ==1){
                ans.push_back(word);
            }
        }

【5】1010. 总持续时间可被 60 整除的歌曲

日期:10.20

1.题目链接:1010. 总持续时间可被 60 整除的歌曲 - 力扣(LeetCode)https://leetcode.cn/problems/pairs-of-songs-with-total-durations-divisible-by-60/description/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,计数,组合计数

3.方法一:组合数学(半解)

每首歌曲对结果的影响因素是它的持续时间除以 60 后的余数。可以用一个长度为 60 的数组 cnt,用来表示余数出现的次数。然后分情况统计歌曲对:

关键代码:

cpp 复制代码
        vector<int> cnt(60);
        for(int t:time){
            cnt[t%60]++;  // 统计每个时间对60取模的余数
        }       
        long long res=0;  // 使用long long防止整数溢出       
        // 处理余数1到29的情况(与59到31配对)
        for(int i=1;i<30;i++){
            res+=cnt[i]*cnt[60-i];  // 配对数量 = 余数i的数量 × 余数(60-i)的数量
        }
        
        // 处理特殊情况:余数0和余数30
        res+=(long long)cnt[0]*(cnt[0]-1)/2+  // 余数0内部配对:组合数C(n,2)
             (long long)cnt[30]*(cnt[30]-1)/2; // 余数30内部配对:组合数C(n,2)

【6】1079. 活字印刷

日期:10.21

1.题目链接:1079. 活字印刷 - 力扣(LeetCode)https://leetcode.cn/problems/letter-tile-possibilities/description/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,回溯,深度优先搜索

3.方法一:回溯(官方题解)

使用深度优先搜索(DFS)生成所有可能的序列

通过字符计数确保不会超过可用字符的数量

使用回溯法探索所有可能的排列组合

关键代码:

cpp 复制代码
     // DFS函数:递归生成所有可能的序列
    int dfs(unordered_map<char,int>& count,set<char>& tile,int i){
        // 基线条件:没有剩余字符可用
        if(i==0){
            return 1; 
        }
        int res=1; // 从1开始,表示当前序列
        for(char t:tile){
            if(count[t]>0){ 
                count[t]--; // 使用一个该字符
                res+=dfs(count,tile,i-1); // 递归处理剩余字符
                count[t]++; // 回溯,恢复字符计数
            }
        }

【7】1090. 受标签影响的最大值

日期:10.22

1.题目链接:1090. 受标签影响的最大值 - 力扣(LeetCode)https://leetcode.cn/problems/largest-values-from-labels/solutions/2278874/shou-biao-qian-ying-xiang-de-zui-da-zhi-5h9ll/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,计数,排序

3.方法一:排序 + 哈希表(一次题解)

首先将元素按照 values 的值进行降序排序。待排序完成后,依次遍历每个元素并判断是否选择。使用一个变量 choose 记录已经选择的元素个数,以及一个哈希表记录每一种标签已经选择的元素个数(键表示标签,值表示该标签已经选择的元素个数):

如果 choose=numWanted,直接退出遍历;

如果当前元素的标签在哈希表中对应的值等于 useLimit,忽略这个元素,否则选择这个元素,并更新 choose、哈希表以及答案。

关键代码:

cpp 复制代码
     for(int i=0;i<n&&choose<numWanted;++i){
            int label=labels[id[i]];
            // 如果该标签已达到使用限制,跳过
            if(cnt[label]==useLimit){
                continue;
            }
            // 选择当前物品
            ++choose;
            ans+=values[id[i]];
            ++cnt[label];
        }

【8】1160. 拼写单词

日期:10.23

1.题目链接:1160. 拼写单词 - 力扣(LeetCode)https://leetcode.cn/problems/find-words-that-can-be-formed-by-characters/description/?envType=problem-list-v2&envId=counting

2.题目类型:哈希表,字符串,计数

3.方法一:哈希表(一次题解)

只需要用一个哈希表存储 chars 中每个字母的数量,再用一个哈希表存储 word 中每个字母的数量,最后将这两个哈希表的键值对逐一进行比较即可。

关键代码:

cpp 复制代码
       for(string word : words){
            // 统计当前单词中每个字符的出现频率
            unordered_map<char,int> word_cnt;
            for(char c:word){
                ++word_cnt[c];
            }           
            // 检查当前单词是否可以用 chars 拼写
            bool is_ans=true;
            for(char c:word){
                // 如果 chars 中某个字符的数量不足
                if(chars_cnt[c]<word_cnt[c]){
                    is_ans=false;
                    break;
                }
            }            
            // 如果可以拼写,累加单词长度
            if(is_ans){
                ans+=word.size();
            }

【9】1189. "气球" 的最大数量

日期:10.24

1.题目链接:1189. "气球" 的最大数量 - 力扣(LeetCode)https://leetcode.cn/problems/maximum-number-of-balloons/description/?envType=problem-list-v2&envId=counting

2.题目类型:字符串,计数

3.方法一:统计(一次题解)

构成单词 "balloon" 需要 1 个字母 'b' 、1 个字母 'a' 、2 个字母 'l' 、2 个字母 'o' 、1 个字母 'n',因此只需要统计字符串中字母 'a','b','l','o','n' 的数量即可。其中每个字母 "balloon" 需要两个 'l','o',可以将字母 'l','o' 的数量除以 2,返回字母 'a','b','l','o','n' 中数量最小值即为可以构成的单词数量。

关键代码:

cpp 复制代码
        for(auto & ch: text){
            if(ch=='b'){
                cnt[0]++;
            }else if(ch=='a'){
                cnt[1]++;
            }else if(ch=='l'){
                cnt[2]++;
            }else if(ch=='o'){
                cnt[3]++;
            }else if(ch=='n'){
                cnt[4]++;
            }
        }
        cnt[2]/=2;
        cnt[3]/=2;

【10】1221. 分割平衡字符串

日期:10.25

1.题目链接:1221. 分割平衡字符串 - 力扣(LeetCode)https://leetcode.cn/problems/split-a-string-in-balanced-strings/description/?envType=problem-list-v2&envId=counting

2.题目类型:贪心,字符串,计数

3.方法一:贪心(半解)

根据题意,对于一个平衡字符串 s,若 s 能从中间某处分割成左右两个子串,若其中一个是平衡字符串,则另一个的 L 和 R 字符的数量必然是相同的,所以也一定是平衡字符串。

为了最大化分割数量,可以不断循环,每次从 s 中分割出一个最短的平衡前缀,由于剩余部分也是平衡字符串,将其当作 s 继续分割,直至 s 为空时,结束循环。

代码实现中,可以在遍历 s 时用一个变量 d 维护 L 和 R 字符的数量之差,当 d=0 时就说明找到了一个平衡字符串,将答案加一。

关键代码:

cpp 复制代码
        int ans=0,d=0;
        for(char ch : s){
            ch=='L'?++d:--d;
            if(d==0){
                ++ans;
            }
        }
相关推荐
王哈哈^_^3 小时前
【数据集】【YOLO】【目标检测】建筑垃圾数据集 4256 张,YOLO建筑垃圾识别算法实战训推教程。
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·数据集
CoovallyAIHub3 小时前
不看异常,怎么学会识别异常?用“异常”指导异常检测!——NAGL方法解析(附代码地址)
深度学习·算法·计算机视觉
共享家95274 小时前
数据结构-并查集
数据结构·c++·算法
IT古董4 小时前
【第五章:计算机视觉-项目实战之推荐/广告系统】2.粗排算法-(2)理解粗排模型之离线部分:双塔模型结构精讲及实现
人工智能·算法·计算机视觉
茉莉玫瑰花茶4 小时前
贪心 - 后篇
算法
m0_748233644 小时前
【C++篇】C++11入门:踏入C++新世界的大门
java·c++·算法
lxmyzzs4 小时前
【图像算法 - 31】基于深度学习的太阳能板缺陷检测系统:YOLOv12 + UI界面 + 数据集实现
人工智能·深度学习·算法·yolo·缺陷检测
lxmyzzs4 小时前
【图像算法 - 32】基于深度学习的风力发电设备缺陷检测系统:YOLOv12 + UI界面 + 数据集实现
深度学习·算法·yolo·计算机视觉
on_pluto_5 小时前
GAN生成对抗网络学习-例子:生成逼真手写数字图
人工智能·深度学习·神经网络·学习·算法·机器学习·生成对抗网络