「前端刷题」49. 字母异位词分组

277 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

题目

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

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母都恰好只用一次。

 

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""] 输出: [[""]]

示例 3:

输入: strs = ["a"] 输出: [["a"]]

 

提示:

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

思路

质数

解题思路

  • 算术基本定理
    • 任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
  • [a, z]Unicode编码 - 97=[0, 25] 对应26个质数。每字母代表质数乘积表示字符串
  • 乘法交换律忽略字母顺序。算术基本定理保证不同质数 或 相同质数不同个数,乘积唯一

代码

Map

var groupAnagrams = function(strs) {
    var h = new Map, 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]
    for(var i = 0; i < strs.length; i++) {
        for(var j = 0, sum = 1; j < strs[i].length; j++)
            sum *= prime[strs[i].charCodeAt(j) - 97]
        h.has(sum) ? h.get(sum).push(strs[i]) : h.set(sum, [strs[i]])
    }
    return Array.from(h.values())
};

质数解法·Map运行结果

Object

var groupAnagrams = function(strs) {
    var h = Object.create(null), 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]
    for(var i = 0; i < strs.length; i++) {
        for(var j = 0, sum = 1; j < strs[i].length; j++) 
            sum *= prime[strs[i][j].charCodeAt() - 97]
        h[sum] ? h[sum].push(strs[i]) : h[sum] = [strs[i]]
    }
    return Object.values(h)
};

质数解法·Object运行结果

链接

var getPrimes = function(n) {// getPrimes(102) 可以获得26个质数
    var isCom= new Uint8Array(n), b = Math.sqrt(n), r = n > 2 ? [2] : [], t
    for(var i = 3; i < n; i += 2) {
        if(!isCom[i]) {
            r.push(i)
            if (i <= b) for(var j = i; (t = i * j) < n; j += 2) isCom[t] = 1
        }
    }
    return r
};

Sort

解题思路

  • Sort忽略参数,按元素Unicode位点排序,即Unicode编码升序排列

代码

Map

var groupAnagrams = function(strs) {
    var h = new Map, k
    for(var i = 0; i < strs.length; i++) 
        h.has(k = strs[i].split('').sort().join('')) ? h.get(k).push(strs[i]) : h.set(k, [strs[i]])
    return Array.from(h.values())
};

Sort解法Map运行结果

Object

var groupAnagrams = function(strs) {
    var h = Object.create(null), k
    for(var i = 0; i < strs.length; i++) 
        h[k = strs[i].split('').sort().join('')] ? h[k].push(strs[i]) : h[k] = [strs[i]]
    return Object.values(h)
};

Sort解法Object运行结果

数组

解题思路

  • 26长度数组[0, 25]位的对应字母[a, z]出现次数

代码

Map

var groupAnagrams = function(strs) {
    var h = new Map, sum
    for(var i = 0; i < strs.length; i++) {
        for(var j = 0, counts = new Uint8Array(26); j < strs[i].length; j++)
            counts[strs[i][j].charCodeAt() - 97]++
        h.has(sum = counts.toString()) ? h.get(sum).push(strs[i]) : h.set(sum, [strs[i]])
    }
    return Array.from(h.values())
};

数组解法Map运行结果

Object

var groupAnagrams = function(strs) {
    var h = Object.create(null), sum
    for(var i = 0; i < strs.length; i++) {
        for(var j = 0, counts = new Uint8Array(26); j < strs[i].length; j++)
            counts[strs[i][j].charCodeAt() - 97]++
        h[sum = counts.toString()] ? h[sum].push(strs[i]) : h[sum] = [strs[i]]
    }
    return Object.values(h)
};

数组解法Object运行结果

拓展

遍历字符串,字节转Unicode编码:

  • str[i].charCodeAt(0) str.charAt(i).charCodeAt(0) str.charCodeAt(i) 谁更快?
// Setup
const ar = Array.from({length: 100}, _ => String.fromCharCode(...randArray(500, 97, 97 + 26)))
function randArray(len, min, max) {
    return Array.from({length: len}, _ => min + Math.random() * (max - min) | 0)
}
const str2unicode1 = str => {
    let res = '', i = -1
    while(++i < str.length) res += str[i].charCodeAt(0)
    return res
}
const str2unicode2 = str => {
    let res = '', i = -1
    while(++i < str.length) res += str.charAt(i).charCodeAt(0)
    return res
}
const str2unicode3 = str => {
    let res = '', i = -1
    while(++i < str.length) res += str.charCodeAt(i)
    return res
}
// str[i].charCodeAt(0)
for(var i = 0; i < 100; i++) str2unicode1(ar[i])
// str.charAt(i).charCodeAt(0)
for(var i = 0; i < 100; i++) str2unicode2(ar[i])
// str.charCodeAt(i)
for(var i = 0; i < 100; i++) str2unicode3(ar[i])

测试结果
str.charCodeAt(i)更快