【状态压缩 并集查找 图论】2157. 字符串分组

本文涉及知识点

状态压缩 并集查找 图论

LeetCode2157. 字符串分组

给你一个下标从 0 开始的字符串数组 words 。每个字符串都只包含 小写英文字母 。words 中任意一个子串中,每个字母都至多只出现一次。
如果通过以下操作之一,我们可以从 s1 的字母集合得到 s2 的字母集合,那么我们称这两个字符串为 关联的 :
往 s1 的字母集合中添加一个字母。
从 s1 的字母集合中删去一个字母。
将 s1 中的一个字母替换成另外任意一个字母(也可以替换为这个字母本身)。
数组 words 可以分为一个或者多个无交集的 组 。如果一个字符串与另一个字符串关联,那么它们应当属于同一个组。
注意,你需要确保分好组后,一个组内的任一字符串与其他组的字符串都不关联。可以证明在这个条件下,分组方案是唯一的。
请你返回一个长度为 2 的数组 ans :
ans[0] 是 words 分组后的 总组数 。
ans[1] 是字符串数目最多的组所包含的字符串数目。
示例 1:
输入:words = [“a”,“b”,“ab”,“cde”]
输出:[2,3]
解释:

  • words[0] 可以得到 words[1] (将 ‘a’ 替换为 ‘b’)和 words[2] (添加 ‘b’)。所以 words[0] 与 words[1] 和 words[2] 关联。
  • words[1] 可以得到 words[0] (将 ‘b’ 替换为 ‘a’)和 words[2] (添加 ‘a’)。所以 words[1] 与 words[0] 和 words[2] 关联。
  • words[2] 可以得到 words[0] (删去 ‘b’)和 words[1] (删去 ‘a’)。所以 words[2] 与 words[0] 和 words[1] 关联。
  • words[3] 与 words 中其他字符串都不关联。
    所以,words 可以分成 2 个组 [“a”,“b”,“ab”] 和 [“cde”] 。最大的组大小为 3 。
    示例 2:
    输入:words = [“a”,“ab”,“abc”]
    输出:[1,3]
    解释:
  • words[0] 与 words[1] 关联。
  • words[1] 与 words[0] 和 words[2] 关联。
  • words[2] 与 words[1] 关联。
    由于所有字符串与其他字符串都关联,所以它们全部在同一个组内。
    所以最大的组大小为 3 。
    提示:

1 <= words.length <= 2 * 104
1 <= words[i].length <= 26
words[i] 只包含小写英文字母。
words[i] 中每个字母最多只出现一次。

状态压缩

由于本题words[i] 只包括小写字母,且没有重复字母,我们可以对字母集合进行状态压缩, maks&(1<<j) 表示 字母集合存在 ‘a’+j 。
mMask,key 表示某个字母集合 value是一个vector 记录words[i]的下标i。key和words[i]关联。

然后通过 key value建立 并集查找。
注意
一,key 必须是words中存在的字母集合。
二,(mask | (1 << j1)) ^ ( 1 << j2) 括号不能省略
三,建立并集查找时,只需要v[i]和v[i-1]连接,不需要两两连接。前者时间复杂度是O(n),后者时间复杂度是O(nn)。

代码

核心代码

class CUnionFind
{
public:CUnionFind(int iSize) :m_vNodeToRegion(iSize){for (int i = 0; i < iSize; i++){m_vNodeToRegion[i] = i;}m_iConnetRegionCount = iSize;}	CUnionFind(vector<vector<int>>& vNeiBo):CUnionFind(vNeiBo.size()){for (int i = 0; i < vNeiBo.size(); i++) {for (const auto& n : vNeiBo[i]) {Union(i, n);}}}int GetConnectRegionIndex(int iNode){int& iConnectNO = m_vNodeToRegion[iNode];if (iNode == iConnectNO){return iNode;}return iConnectNO = GetConnectRegionIndex(iConnectNO);}void Union(int iNode1, int iNode2){const int iConnectNO1 = GetConnectRegionIndex(iNode1);const int iConnectNO2 = GetConnectRegionIndex(iNode2);if (iConnectNO1 == iConnectNO2){return;}m_iConnetRegionCount--;if (iConnectNO1 > iConnectNO2){UnionConnect(iConnectNO1, iConnectNO2);}else{UnionConnect(iConnectNO2, iConnectNO1);}}bool IsConnect(int iNode1, int iNode2){return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);}int GetConnetRegionCount()const{return m_iConnetRegionCount;}vector<int> GetNodeCountOfRegion()//各联通区域的节点数量{const int iNodeSize = m_vNodeToRegion.size();vector<int> vRet(iNodeSize);for (int i = 0; i < iNodeSize; i++){vRet[GetConnectRegionIndex(i)]++;}return vRet;}std::unordered_map<int, vector<int>> GetNodeOfRegion(){std::unordered_map<int, vector<int>> ret;const int iNodeSize = m_vNodeToRegion.size();for (int i = 0; i < iNodeSize; i++){ret[GetConnectRegionIndex(i)].emplace_back(i);}return ret;}
private:void UnionConnect(int iFrom, int iTo){m_vNodeToRegion[iFrom] = iTo;}vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引int m_iConnetRegionCount;
};class Solution {
public:vector<int> groupStrings(vector<string>& words) {unordered_map<int, vector<int>> mMask;for (int i = 0; i < words.size(); i++) {int iMask = 0;for (const auto& ch : words[i]) {iMask |= (1 << (ch - 'a'));}mMask[iMask].emplace_back(i);}		for (const auto& [mask, tmp] : mMask) {auto AddNewMask = [&](int mask,const int iMask) {if (!mMask.count(iMask)) { return; }mMask[mask].emplace_back(mMask[iMask][0]);};for (int j = 0; j < 26; j++) {if (mask & (1 << j)) {AddNewMask(mask, mask ^ (1 << j));}else {AddNewMask(mask, mask | (1 << j));}}for (int j1 = 0; j1 < 26; j1++) {if (mask & (1 << j1)) { continue; }for (int j2 = 0; j2 < 26; j2++) {if (!(mask & (1 << j2))) { continue; }AddNewMask(mask, (mask | (1 << j1)) ^ ( 1 << j2));}}}CUnionFind uf(words.size());for (const auto& [mask, v] : mMask) {for (int i = 1; i < v.size(); i++) {uf.Union(v[i], v[i - 1]);}}auto m = uf.GetNodeOfRegion();vector<int> vRet = { (int)m.size() };int iMax = 0;for (const auto& [tmp, v] : m) {iMax = max(iMax, (int)v.size());}vRet.emplace_back(iMax);return vRet;}
};

测试用例

class CUnionFind
{
public:CUnionFind(int iSize) :m_vNodeToRegion(iSize){for (int i = 0; i < iSize; i++){m_vNodeToRegion[i] = i;}m_iConnetRegionCount = iSize;}	CUnionFind(vector<vector<int>>& vNeiBo):CUnionFind(vNeiBo.size()){for (int i = 0; i < vNeiBo.size(); i++) {for (const auto& n : vNeiBo[i]) {Union(i, n);}}}int GetConnectRegionIndex(int iNode){int& iConnectNO = m_vNodeToRegion[iNode];if (iNode == iConnectNO){return iNode;}return iConnectNO = GetConnectRegionIndex(iConnectNO);}void Union(int iNode1, int iNode2){const int iConnectNO1 = GetConnectRegionIndex(iNode1);const int iConnectNO2 = GetConnectRegionIndex(iNode2);if (iConnectNO1 == iConnectNO2){return;}m_iConnetRegionCount--;if (iConnectNO1 > iConnectNO2){UnionConnect(iConnectNO1, iConnectNO2);}else{UnionConnect(iConnectNO2, iConnectNO1);}}bool IsConnect(int iNode1, int iNode2){return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);}int GetConnetRegionCount()const{return m_iConnetRegionCount;}vector<int> GetNodeCountOfRegion()//各联通区域的节点数量{const int iNodeSize = m_vNodeToRegion.size();vector<int> vRet(iNodeSize);for (int i = 0; i < iNodeSize; i++){vRet[GetConnectRegionIndex(i)]++;}return vRet;}std::unordered_map<int, vector<int>> GetNodeOfRegion(){std::unordered_map<int, vector<int>> ret;const int iNodeSize = m_vNodeToRegion.size();for (int i = 0; i < iNodeSize; i++){ret[GetConnectRegionIndex(i)].emplace_back(i);}return ret;}
private:void UnionConnect(int iFrom, int iTo){m_vNodeToRegion[iFrom] = iTo;}vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引int m_iConnetRegionCount;
};class Solution {
public:vector<int> groupStrings(vector<string>& words) {unordered_map<int, vector<int>> mMask;for (int i = 0; i < words.size(); i++) {int iMask = 0;for (const auto& ch : words[i]) {iMask |= (1 << (ch - 'a'));}mMask[iMask].emplace_back(i);}		for (const auto& [mask, tmp] : mMask) {auto AddNewMask = [&](int mask,const int iMask) {if (!mMask.count(iMask)) { return; }mMask[mask].emplace_back(mMask[iMask][0]);};for (int j = 0; j < 26; j++) {if (mask & (1 << j)) {AddNewMask(mask, mask ^ (1 << j));}else {AddNewMask(mask, mask | (1 << j));}}for (int j1 = 0; j1 < 26; j1++) {if (mask & (1 << j1)) { continue; }for (int j2 = 0; j2 < 26; j2++) {if (!(mask & (1 << j2))) { continue; }AddNewMask(mask, (mask | (1 << j1)) ^ ( 1 << j2));}}}CUnionFind uf(words.size());for (const auto& [mask, v] : mMask) {for (int i = 1; i < v.size(); i++) {uf.Union(v[i], v[i - 1]);}}auto m = uf.GetNodeOfRegion();vector<int> vRet = { (int)m.size() };int iMax = 0;for (const auto& [tmp, v] : m) {iMax = max(iMax, (int)v.size());}vRet.emplace_back(iMax);return vRet;}
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{vector<string> words;{Solution sln;words = { "a", "b" };auto res = sln.groupStrings(words);Assert({ 1,2 }, res);}{Solution sln;words = { "a", "b", "ab", "cde" };auto res = sln.groupStrings(words);Assert({ 2,3 }, res);}{Solution sln;words = { "a","ab","abc" };auto res = sln.groupStrings(words);Assert({ 1,3 }, res);}
}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/636273.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

强固型工业电脑在称重系统+叉车电脑,称重量体扫码一体机,物流分拣线工作站行业应用

称重系统叉车电脑行业应用 背景介绍 在叉车上安装称重传感器&#xff0c;通过对举升压力的自动检测&#xff0c;将压力信号转换为电流或电压信号&#xff0c;经过A/D转换&#xff0c;使模拟信号变为数字信号&#xff0c;经微处理器进行数据处理后通过蓝牙、串口或者USB接口将称…

【AI自媒体制作】【AI工具】Midjourney中文站

Midjourney Midjourney中文站, MJ中文站 - 专业AI绘图网站 广场 绘画广场&#xff1a; 包含大量其他用户生成好的图片&#xff0c;可以自由保存。 视频广场&#xff1a; 普通用户目前只支持查看&#xff0c;无法下载 画夹广场&#xff1a; 有很多免费的画夹&#xff0c;比…

(1)认识人工智能

第一章 认识人工智能 引言 本人目前大三&#xff0c;双非一本的人工智能专业&#xff0c;代码能力不算太差&#xff0c;做过项目&#xff0c;也打了比赛&#xff0c;获了奖&#xff0c;但是走技术路线总会有否定自己的感觉&#xff0c;可能是感觉自己的才能没有在搞技术方面实…

Nacos注册中心实战

注册中心实战 1.快速使用1.1 版本选择1.2 父pom1.3 nacos-client pom1.4 nacos-client bootstrap.yaml配置 2.常用配置3.Nacos Server环境搭建3.1 单机模式启动3.1.1 解压&#xff0c;进入nacos目录 进入bin目录下&#xff0c;编辑startup.cmd脚本&#xff0c; 设置启动模式为单…

数据仓库作业五:第8章 关联规则挖掘

目录 第8章 关联规则挖掘作业题 第8章 关联规则挖掘 作业题 1、设4-项集 X { a , b , c , d } X\{a,b,c,d\} X{a,b,c,d}&#xff0c;试求出由 X X X 导出的所有关联规则。 解&#xff1a; 首先生成项集的所有非空真子集。这包括&#xff1a; { a } , { b } , { c } , {…

vivado 在波形查看器中查看 ILA 探针数据

在波形查看器中查看 ILA 探针数据 Vivado 集成设计环境 (IDE) 中的“ ILA 波形查看器 (ILA waveform viewer) ”提供了一种强大的方法 &#xff0c; 可分析从 ILA 调 试核采集的数据。成功触发 ILA 核并采集数据后 &#xff0c; Vivado 会以从 ILA 核收集的数据自动填…

solidity入门

Solidity 是以太坊智能合约开发的主要编程语言&#xff0c;支持多种数据类型&#xff0c;其中数组是一种非常常用和灵活的数据结构。在本教程中&#xff0c;我们将深入探讨 Solidity 中数组的各种类型、创建规则以及常见操作。 ### 固定长度数组 固定长度数组在声明时指定了数…

前端css中table表格的属性使用

前端css中table表格的属性使用 一、前言二、常见的表格属性1.边框的样式2.布局和对齐3.间距和填充4.背景和颜色5.字体的样式6.边框的圆角 三、简单的表格&#xff0c;例子11.源码12.源码1效果截图 四、给表格添加动画效果&#xff0c;例子21.源码22.源码2的运行效果 五、结语六…

Linux文件系统 软硬链接

文章目录 文件背景知识磁盘文件磁盘物理结构磁盘存储结构对磁盘的存储进行逻辑抽象Boot BlockSuper blockData blocksInode TableBlcokBitmapinode BitmapGroup Descriptor Table 文件名和inode编号创建文件删除文件查找文件 软硬链接软链接硬链接查看文件信息stat命令取消软硬…

数据结构 -- 二叉树二叉搜索树

二叉树 二叉树是这么一种树状结构&#xff1a;每个节点最多有两个孩子&#xff0c;左孩子和右孩子 重要的二叉树结构 完全二叉树&#xff08;complete binary tree&#xff09;是一种二叉树结构&#xff0c;除最后一层以外&#xff0c;每一层都必须填满&#xff0c;填充时要遵…

百科不全书之 docker记录

docker记录 1.参考文件2. Docker简介与虚拟机的区别 3. 安装Docker注意 Windows家庭版的要额外设置 4.使用5.docker与ROS 1.参考文件 参考视频&#xff1a;B站【GeekHour】Docker入门教程: 【GeekHour】30分钟Docker入门教程 2. Docker简介 Docker是一个用于构建运行 传送…

【智能算法】吉萨金子塔建造算法(GPC)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年&#xff0c;S Harifi等人受到观古代遗迹构造启发&#xff0c;提出了吉萨金子塔建造算法&#xff08;Giza Pyramids Construction, GPC&#xff09;。 2.算法原理 2.1算法思想 GPC模拟了古埃…