力扣LeetCode: 2506 统计相似字符串对的数目

devtools/2025/2/23 5:50:27/

题目:

给你一个下标从 0 开始的字符串数组 words 。

如果两个字符串由相同的字符组成,则认为这两个字符串 相似 。

  • 例如,"abca" 和 "cba" 相似,因为它们都由字符 'a''b''c' 组成。
  • 然而,"abacba" 和 "bcfd" 不相似,因为它们不是相同字符组成的。

请你找出满足字符串 words[i]  words[j] 相似的下标对 (i, j) ,并返回下标对的数目,其中 0 <= i < j <= words.length - 1 。

示例 1:

输入:words = ["aba","aabb","abcd","bac","aabc"]
输出:2
解释:共有 2 对满足条件:
- i = 0 且 j = 1 :words[0] 和 words[1] 只由字符 'a' 和 'b' 组成。 
- i = 3 且 j = 4 :words[3] 和 words[4] 只由字符 'a'、'b' 和 'c' 。 

示例 2:

输入:words = ["aabb","ab","ba"]
输出:3
解释:共有 3 对满足条件:
- i = 0 且 j = 1 :words[0] 和 words[1] 只由字符 'a' 和 'b' 组成。 
- i = 0 且 j = 2 :words[0] 和 words[2] 只由字符 'a' 和 'b' 组成。 
- i = 1 且 j = 2 :words[1] 和 words[2] 只由字符 'a' 和 'b' 组成。 

示例 3:

输入:words = ["nba","cba","dba"]
输出:0
解释:不存在满足条件的下标对,返回 0 。

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 100
  • words[i] 仅由小写英文字母组成

解法:

解决思路

  1. 提取字符集合

    • 对于每个字符串,提取其中包含的字符种类(去重)。

    • 例如,"aba" 的字符集合是 {'a', 'b'}"aabb" 的字符集合也是 {'a', 'b'}

  2. 比较字符集合

    • 如果两个字符串的字符集合相同,则它们相似。

    • 例如,"aba" 和 "aabb" 的字符集合都是 {'a', 'b'},因此它们相似。

  3. 统计相似对数

    • 对于所有字符串,统计具有相同字符集合的字符串数量。

    • 如果有 k 个字符串具有相同的字符集合,则这些字符串之间可以形成 C(k, 2) 对相似对(即从 k 个字符串中选 2 个的组合数)。


实现步骤

  1. 提取字符集合

    • 对每个字符串,将其字符去重并排序,生成一个唯一的标识符(例如,将字符集合转换为字符串)。

    • 例如,"aba" 的字符集合是 {'a', 'b'},可以转换为字符串 "ab"

  2. 统计字符集合的出现次数

    • 使用哈希表(unordered_map)记录每个字符集合的出现次数。

    • 例如,"ab" 出现 2 次,"abc" 出现 1 次。

  3. 计算相似对数

    • 对于哈希表中的每个字符集合,如果有 k 个字符串具有相同的字符集合,则这些字符串之间可以形成 k * (k - 1) / 2 对相似对。

    • 将所有字符集合的相似对数累加,得到最终结果。


代码实现

class Solution {
public:int similarPairs(vector<string>& words) {unordered_map<string, int> countMap;// 遍历每个字符串,提取字符集合并统计for (const string& word : words) {string charSet = getCharSet(word);countMap[charSet]++;}int result = 0;// 计算满足条件的下标对数量for (const auto& pair : countMap) {int k = pair.second;if (k >= 2) {result += k * (k - 1) / 2;}}return result;}private:string getCharSet(const string& word) {vector<char> chars(word.begin(), word.end());sort(chars.begin(), chars.end());chars.erase(unique(chars.begin(), chars.end()), chars.end());return string(chars.begin(), chars.end());}
};

代码详细解释

  1. getCharSet 函数

    • 输入:一个字符串 word

    • 输出:该字符串的字符集合(去重并排序后的字符串)。

    • 实现步骤:

      • 将字符串转换为字符数组 chars

      • 对字符数组排序(sort)。

      • 去重(unique 和 erase)。

      • 将字符数组转换为字符串并返回。

    示例

    • 输入:"aba"

    • 输出:"ab"

  2. similarPairs 函数

    • 输入:字符串数组 words

    • 输出:满足条件的下标对数量。

    • 实现步骤:

      • 遍历每个字符串,调用 getCharSet 获取字符集合。

      • 使用哈希表 countMap 统计每个字符集合的出现次数。

      • 遍历哈希表,计算每个字符集合的相似对数,并累加到结果中。

    示例

    • 输入:["aba", "aabb", "abcd", "bac", "aabc"]

    • 哈希表内容:{"ab": 2, "abcd": 1, "abc": 2}

    • 计算结果:C(2, 2) + C(2, 2) = 1 + 1 = 2


复杂度分析

  1. 时间复杂度

    • 提取字符集合:对于每个字符串,排序和去重的复杂度为 O(m log m),其中 m 是字符串的平均长度。

    • 统计哈希表:遍历所有字符串的复杂度为 O(n),其中 n 是字符串的数量。

    • 计算相似对数:遍历哈希表的复杂度为 O(n)

    • 总时间复杂度:O(n * m log m)

  2. 空间复杂度

    • 哈希表 countMap 的空间复杂度为 O(n)

    • 字符集合的空间复杂度为 O(m)

    • 总空间复杂度:O(n * m)


示例运行

示例 1:

输入:words = ["aba", "aabb", "abcd", "bac", "aabc"]

  1. 提取字符集合:

    • "aba" -> "ab"

    • "aabb" -> "ab"

    • "abcd" -> "abcd"

    • "bac" -> "abc"

    • "aabc" -> "abc"

  2. 统计哈希表:

    • {"ab": 2, "abcd": 1, "abc": 2}

  3. 计算相似对数:

    • "ab" 出现 2 次 -> C(2, 2) = 1

    • "abc" 出现 2 次 -> C(2, 2) = 1

    • 总相似对数:1 + 1 = 2

输出:2


示例 2:

输入:words = ["aabb", "ab", "ba"]

  1. 提取字符集合:

    • "aabb" -> "ab"

    • "ab" -> "ab"

    • "ba" -> "ab"

  2. 统计哈希表:

    • {"ab": 3}

  3. 计算相似对数:

    • "ab" 出现 3 次 -> C(3, 2) = 3

    • 总相似对数:3

输出:3


示例 3:

输入:words = ["nba", "cba", "dba"]

  1. 提取字符集合:

    • "nba" -> "abn"

    • "cba" -> "abc"

    • "dba" -> "abd"

  2. 统计哈希表:

    • {"abn": 1, "abc": 1, "abd": 1}

  3. 计算相似对数:

    • 每个字符集合只出现 1 次,无法形成相似对。

    • 总相似对数:0

输出:0


总结

通过提取字符集合、统计哈希表,并计算组合数,我们可以高效地解决这个问题。代码的时间复杂度和空间复杂度都在合理范围内,能够处理题目中的最大输入规模。


http://www.ppmy.cn/devtools/161111.html

相关文章

值传递,引用传递

在Java中&#xff0c;值传递和引用传递是两种不同的参数传递方式&#xff0c;尽管存在一些关于Java是否支持引用传递的争议。 值传递&#xff08;Pass by Value&#xff09; 值传递意味着当你调用一个方法时&#xff0c;方法参数接收到的是调用时传入的实际值的副本。换句话说…

【清华大学】DeepSeek从入门到精通完整版pdf下载

DeepSeek从入门到精通.pdf 一共104页完整版 下载链接: https://pan.baidu.com/s/1-gnkTTD7EF2i_EKS5sx4vg?pwd1234 提取码: 1234 或 链接&#xff1a;https://pan.quark.cn/s/79118f5ab0fd 一、DeepSeek 概述 背景与定位 DeepSeek 的研发背景 核心功能与技术特点&#xff08…

【Mastering Vim 2_05】第四章:深入理解 Vim 的结构化文本

【最新版《Mastering Vim》封面&#xff0c;涵盖 Vim 9.0 版特性】 文章目录 第四章 深入理解结构化文本1 Vim 内置的自动补全功能2 YouCompleteMe 插件对自动补全的增强3 tags 文件的用法4 Exuberant Ctags 简介5 借助 Undotree 插件实现 Vim 撤销树的可视化 写在前面 本章围绕…

Windows 快速搭建C++开发环境,安装C++、CMake、QT、Visual Studio、Setup Factory

安装C 简介 Windows 版的 GCC 有三个选择&#xff1a; CygwinMinGWmingw-w64 Cygwin、MinGW 和 mingw-w64 都是在 Windows 操作系统上运行的工具集&#xff0c;用于在 Windows 环境下进行开发和编译。 Cygwin 是一个在 Windows 上运行的开源项目&#xff0c;旨在提供类Uni…

JavaScript异步编程方式多,区别是什么?

在JavaScript中&#xff0c;常见的异步编程方式有回调函数、Promise、Generator函数和async/await&#xff0c;以下用大白话介绍它们的区别并给出代码示例&#xff1a; 回调函数 概念&#xff1a;就是把一个函数当作参数传给另一个函数&#xff0c;等那个函数完成任务后再调用…

前端新手如何从CtrlC+V开始?(前端开源UI平台汇总)

前言 如果你是个前端小白&#xff0c;面对一堆满屏的div标签和css就头晕眼花&#xff1f;别担心&#xff0c;咱都是从“代码搬运工” 开始的。当你的同桌还在和flex布局玩"你动我猜"的时候&#xff0c;你已经像拼乐高一样把现成的按钮组件搭成炫酷界面。这可不是作弊…

RabbitMq 基础

文章目录 一、初识 MQ 1.1 同步调用&#xff1a;1.2 异步调用&#xff1a; 二、RabbitMQ三、SpringAMQP 3.1 依赖和配置文件3.2 消息发送和接收&#xff1a; 3.2.1 消息发送&#xff1a;3.2.2 消息接收&#xff1a; 3.3 WorkQueues 模型&#xff1a;3.4 交换机类型&#xff1a…

Git环境搭建指南

Git 是当今最流行的版本控制系统&#xff0c;无论是个人开发还是团队协作都离不开它。本文将从零开始&#xff0c;手把手教你 在Mac、Windows、Linux三大操作系统上快速搭建Git环境&#xff0c;并验证安装是否成功。 # 一、Mac系统安装Git # 方法1&#xff1a;通过Homebrew安装…