力扣1002题C++解法详解

问题解构与核心逻辑

给定一个字符串数组 words,要求返回一个列表,其中包含在所有字符串中都出现的字符。字符区分大小写,题目说明中均为小写字母。关键约束如下:

  1. 共有性 :结果中的每个字符必须在数组中的每一个字符串里都出现过。
  2. 最小频率 :若一个字符在不同字符串中出现的次数不同,则它在结果中出现的次数应为在所有字符串中出现次数的最小值
  3. 输出 :结果以字符串列表(vector<string>)形式返回,每个字符作为一个独立的字符串元素。

例如,输入 words = ["bella","label","roller"],字符 'l' 在三个字符串中分别出现2、2、2次,因此结果中加入两个 "l";字符 'e' 分别出现1、1、1次,结果中加入一个 "e"。输出为 ["e","l","l"]

C++解法:数组计数法

由于字符限定为小写字母,可以使用长度为26的整型数组作为哈希表来统计字符频率,这是最高效的方法。

算法步骤

  1. 初始化最小频率数组 :创建一个 vector<int> minFreq(26, INT_MAX),用于记录每个字符('a' 到 'z')在所有字符串中的最小出现次数。初始值设为极大值,便于后续取最小值。
  2. 遍历字符串数组
    • 对每个字符串 word,创建一个临时数组 charCount(26, 0) 统计其字符频率。
    • 遍历 word 中的每个字符,对应 charCount 索引(c - 'a')加1。
    • 遍历26个字母,将 minFreq 的每个位置更新为 min(minFreq[i], charCount[i])
  3. 构建结果 :遍历 minFreq 数组,将出现次数大于0的字符,按其最小频率值,重复转换为字符串并添加到结果向量中。

C++代码实现

cpp 复制代码
#include <vector>
#include <string>
#include <algorithm>
#include <climits> // 用于INT_MAX

using namespace std;

class Solution {
public:
    vector<string> commonChars(vector<string>& words) {
        // 步骤1:初始化最小频率数组
        vector<int> minFreq(26, INT_MAX);
        
        // 步骤2:遍历所有单词,更新最小频率
        for (const string& word : words) {
            vector<int> charCount(26, 0);
            // 统计当前单词的字符频率
            for (char c : word) {
                charCount[c - 'a']++;
            }
            // 更新全局最小频率
            for (int i = 0; i < 26; ++i) {
                minFreq[i] = min(minFreq[i], charCount[i]);
            }
        }
        
        // 步骤3:根据最小频率构建结果
        vector<string> result;
        for (int i = 0; i < 26; ++i) {
            // 将字符 (char)('a' + i) 重复 minFreq[i] 次加入结果
            for (int j = 0; j < minFreq[i]; ++j) {
                // 使用 emplace_back 直接在容器末尾构造字符串,效率优于 push_back
                result.emplace_back(1, 'a' + i);
            }
        }
        return result;
    }
};

代码关键点解析

  • minFreq 初始化为 INT_MAX:确保第一次与 charCountmin 时,能正确更新为 charCount 的值。
  • 内层循环 for (char c : word):遍历字符串的简便写法。
  • c - 'a':将字符 'a'-'z' 映射到数组索引 0-25 的标准方法。
  • result.emplace_back(1, 'a' + i):这是构建结果的关键。emplace_back 直接在 result 向量的末尾构造一个字符串对象,该字符串由1个字符 ('a' + i) 组成。这比先创建临时字符串再用 push_back 更高效。

复杂度分析

  • 时间复杂度O(N * M) ,其中 N 是数组 words 的长度,M 是数组中字符串的平均长度。我们需要遍历每个字符串的每个字符两次(一次统计,一次更新最小值),但常数项可以忽略,仍为线性复杂度。
  • 空间复杂度O(1)O(∣Σ∣) ,其中 ∣Σ∣ 是字符集大小(此处为26)。我们只使用了固定大小的辅助数组,与输入数据规模无关。

与其他解法的对比

为了更全面理解,下表将本解法与另一种常见的哈希表(unordered_map)解法进行对比:

特性 数组计数法(推荐) 哈希表法(unordered_map
数据结构 固定大小的 vector<int>(大小26) unordered_map<char, int>
适用场景 字符集已知且较小(如小写字母) 字符集未知或较大(如Unicode)
时间复杂度 O(N * M),访问数组为O(1) O(N * M),哈希表操作平均O(1)
空间复杂度 O(1) O(∣Σ∣),但常数开销更大
性能 更优。内存连续,CPU缓存友好,无哈希冲突开销。 稍差。涉及哈希计算和动态内存管理。
代码简洁性 简单直观 相对复杂,需要处理键不存在的情况

对于本题(仅小写字母),数组计数法是绝对最优选择,其效率远高于哈希表法。

关联题目与模式总结

此题的本质是求多个集合(每个字符串的字符频次)的交集,且交集中元素的重数取其在各集合中出现次数的最小值。这种"计数取最小"的模式可以推广到一系列问题:

题目 核心操作 与本题的关联
1002. 查找常用字符 求多个字符串的字符频率最小交集 本题本身
349. 两个数组的交集 求两个数组的唯一公共元素集合 简化版,只求是否存在,不统计次数
350. 两个数组的交集 II 求两个数组的公共元素,并保留最小出现次数 可以看作本题在两个"字符串"(数组)上的特例
242. 有效的字母异位词 比较两个字符串的字符频率是否完全相同 使用相同的数组计数技术

掌握这种基于固定数组的频次统计与比较方法,是解决许多字符串处理与哈希类问题的基础技能。


参考来源

相关推荐
钟灵9211 小时前
C++【模板初阶】
开发语言·c++·笔记·c#
码不停蹄的玄黓1 小时前
旁路缓存(Cache-Aside,CA)
java·开发语言
江屿风1 小时前
【C++笔记】vector流食般投喂
开发语言·c++·笔记
星恒随风1 小时前
Python 基础语法详解(3):顺序语句、条件语句和循环语句一篇讲清楚
开发语言·笔记·python·学习
CHHH_HHH1 小时前
【C++】红黑树:比AVL树更实用的平衡二叉搜索树
开发语言·数据结构·c++·算法·stl
牛油果子哥q1 小时前
【C++内存对齐与结构体填充】C++内存对齐与结构体填充深度精讲:对齐规则、结构体内存大小计算、填充冗余、笔试真题与工程优化方案
开发语言·c++
ch.ju1 小时前
Java程序设计(第3版)第四章——set-get方法
java·开发语言
Lazionr1 小时前
基础算法 | 模拟算法练习
c++·算法
智能制造产品经理代码提升1 小时前
快速搭建PayPal标准API测试框架
开发语言·lua