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

一、前置知识(可跳过)

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 (包含字母异位词的数组) 转换为一个数组,并返回。

实例与展示

四、结语

再见!

相关推荐
像风一样自由202032 分钟前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
大千AI助手44 分钟前
DTW模版匹配:弹性对齐的时间序列相似度度量算法
人工智能·算法·机器学习·数据挖掘·模版匹配·dtw模版匹配
aiprtem1 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊1 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术1 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing1 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止2 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall2 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴2 小时前
简单入门Python装饰器
前端·python