一分钟吃透一道面试算法题——字母异位词分组(最优解)

一、前置知识(可跳过)

1.质数分解的唯一性

算术基本定理:任何大于1的自然数,要么本身就是一个质数,要么可以唯一地分解成一系列质数的乘积。

在字母异位词分组的场景中, 我们将每个字母映射到一个唯一的质数 ,然后将字符串中所有字母对应的质数相乘。 由于质数分解的唯一性, 确保了只有字母异位词(它们所包含的字符完全相同,只是顺序不同)才会有相同的质数乘积。 这使得我们可以使用这个质数乘积作为键来将字母异位词分组。 这是为什么这种方法可以有效地将字母异位词分组的关键原因。

二、题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

less 复制代码
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

lua 复制代码
输入: strs = [""]
输出: [[""]]

示例 3:

lua 复制代码
输入: strs = ["a"]
输出: [["a"]]

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

三、题解

js 复制代码
var groupAnagrams = function(strs) {
    const anagramGroups = new Map();

    for (const str of strs) {
        // Optimized Key Generation: Prime Number Product
        let key = 1;
        for (let i = 0; i < str.length; i++) {
            const charCode = str.charCodeAt(i) - 'a'.charCodeAt(0);
            // Prime numbers mapped to characters (a=2, b=3, c=5, etc.)
            const prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101][charCode];
            key *= prime;
        }
        // Empty string case:
        if (str.length === 0) key = 0; //Crucial for handling empty strings

        if (anagramGroups.has(key)) {
            anagramGroups.get(key).push(str);
        } else {
            anagramGroups.set(key, [str]);
        }
    }

    return Array.from(anagramGroups.values());
};

核心思想

字母异位词包含相同的字符,只是字符的顺序不同。如果我们能将每个字母异位词映射到一个唯一的"指纹" (或者说键),那么我们就可以用这个键来将它们分组。 这里使用的关键在于将每个字母映射到一个唯一的质数,然后将字符串中所有字母对应的质数相乘,得到的乘积就作为这个字符串的键。 由于质数分解的唯一性,确保了只有字母异位词才会有相同的键。

详细解析

ini 复制代码
/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    const anagramGroups = new Map();
  • 这部分是标准的函数定义和初始化。 anagramGroups 是一个 Map 对象,用于存储最终的结果。Map 对象的 key 是字符串对应的质数乘积,value 是包含相同字母异位词的数组。
rust 复制代码
    for (const str of strs) {
  • 遍历输入的字符串数组 strs
csharp 复制代码
        // Optimized Key Generation: Prime Number Product
        let key = 1;
  • 初始化 key 为 1。注意,这里必须从 1 开始,因为任何数乘以 1 还是它本身。 key 将用于存储当前字符串 str 的质数乘积。
ini 复制代码
        for (let i = 0; i < str.length; i++) {
            const charCode = str.charCodeAt(i) - 'a'.charCodeAt(0);
            // Prime numbers mapped to characters (a=2, b=3, c=5, etc.)
            const prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101][charCode];
            key *= prime;
        }
  • 这个循环遍历当前字符串 str 中的每个字符。

    • const charCode = str.charCodeAt(i) - 'a'.charCodeAt(0);:计算字符的 ASCII 码, 并减去 'a' 的 ASCII 码,得到字符在字母表中的索引(0-25)。 例如,'a' 的 charCode 是 0, 'b' 的 charCode 是 1,以此类推。
    • const prime = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101][charCode];: 使用预定义的质数数组,将每一个字母映射到一个唯一的质数。 'a' 映射到 2, 'b' 映射到 3, 'c' 映射到 5,以此类推。
    • key *= prime;: 将当前字符对应的质数乘以 key。 这样,key 就逐步累积了字符串中所有字符对应的质数的乘积。
ini 复制代码
        // Empty string case:
        if (str.length === 0) key = 0; //Crucial for handling empty strings
  • 关键步骤: 处理空字符串的情况。 如果字符串为空 (str.length === 0), 则将 key 设置为 0。 这样做很重要,原因如下:

    • 如果不特殊处理,空字符串的 key 仍然是 1(因为初始值是 1,并且没有乘以任何质数)。 这会导致所有空字符串都被错误地分组到一起,因为它们都具有相同的 key(即 1)。
    • 将空字符串的 key 设置为 0,确保了空字符串与其他任何非空字符串都能正确区分开。
vbnet 复制代码
        if (anagramGroups.has(key)) {
            anagramGroups.get(key).push(str);
        } else {
            anagramGroups.set(key, [str]);
        }
  • 这部分与之前的代码类似,用于将具有相同 key 的字符串分组。

    • if (anagramGroups.has(key)):检查 Map 中是否已经存在具有相同 key(相同质数乘积)的条目。
    • anagramGroups.get(key).push(str);:如果存在, 则将当前字符串 str 添加到该 key 对应的数组中。
    • else { anagramGroups.set(key, [str]); }: 如果不存在, 则创建一个新的 key,并将当前字符串 str 作为一个新的数组添加到 Map 中。
csharp 复制代码
    }
    return Array.from(anagramGroups.values());
};
  • 循环结束后,anagramGroups包含了所有分组好的字母异位词。 Array.from(anagramGroups.values())Map 中的 values (包含字母异位词的数组) 转换为一个数组,并返回。

实例与展示

四、结语

再见!

相关推荐
元亓亓亓26 分钟前
LeetCode热题100--230. 二叉搜索树中第 K 小的元素--中等
算法·leetcode·职场和发展
草莓熊Lotso26 分钟前
《算法闯关指南:优选算法-双指针》--01移动零,02复写零
c语言·c++·经验分享·算法·leetcode
焜昱错眩..1 小时前
代码随想录算法训练营第三十九天|62.不同路径 63.不同路径ll
算法
dy17173 小时前
element-plus表格默认展开有子的数据
前端·javascript·vue.js
焦耳加热5 小时前
阿德莱德大学Nat. Commun.:盐模板策略实现废弃塑料到单原子催化剂的高值转化,推动环境与能源催化应用
人工智能·算法·机器学习·能源·材料工程
wan5555cn5 小时前
多张图片生成视频模型技术深度解析
人工智能·笔记·深度学习·算法·音视频
u6066 小时前
常用排序算法核心知识点梳理
算法·排序
2501_915918416 小时前
Web 前端可视化开发工具对比 低代码平台、可视化搭建工具、前端可视化编辑器与在线可视化开发环境的实战分析
前端·低代码·ios·小程序·uni-app·编辑器·iphone
程序员的世界你不懂7 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
索迪迈科技7 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育