【剑指Offer】整数(六)二进制 - 单词长度的最大乘积 - JavaScript

161 阅读1分钟

嗨!~ 大家好,我是YK菌 🐷 ,一个微系前端 ✨,爱思考,爱总结,爱记录,爱分享 🏹,欢迎关注我呀 😘 ~ [微信公众号:YK菌]

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 8 天,点击查看活动详情

今天继续来刷《剑指offer(专项突破版)》,原书是Java版本的,这里就是以JavaScript角度来看这些算法题。

剑指 Offer II 005. 单词长度的最大乘积

给定一个字符串数组 words,请计算当两个字符串 words[i]words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0

本题与主站 318 题相同:318. 最大单词长度乘积

分析

最容易想到的就是暴力遍历,遍历数组中每个字符串对是否有公共字符串,没有的话就计算长度乘积,并返回其中最大的结果。

/**
 * @param {string[]} words
 * @return {number}
 */
var maxProduct = function(words) {
    let result = 0
    // 遍历所有的字符串对
    for(let i = 0; i < words.length - 1; i++){
        for(let j = i + 1; j < words.length; j++){
            // 判断字符串对是否有相同元素
            let flag = true
            for(let m = 0; m < words[i].length; m++){
                for(let n = 0; n < words[j].length; n++){
                    if(words[i][m] === words[j][n]){
                        flag = false
                    }
                }
            }
            if(flag){
                let size = words[i].length * words[j].length
                result = Math.max(size, result)
            }
        }
    } 
    return result
};

image.png

分析一下时间复杂度:双层遍历所有字符串对O(n2)O(n^2),比较字符串是否有相同字母O(pq)O(p*q)(其中p、q分别是两个字符串的长度)。所以这个算法的时间复杂度是O(pqn2)O(p*q*n^2)

优化

最先想到的优化的地方就是比较两个字符串是否有相同字符,上面的方法太傻了,太暴力了,我们可以用一个哈希表来优化算法的时间效率。

由于题目给的字符串都是小写字母组成的,所以一共只有26个字符可以选择。因此我们在判断两个字符串是否有相同字符时,只需要从26个字母中判断某个字符是否在两个字符串对应的哈希表中都出现了即可。26是一个常数,所以在哈希表中查找的时间复杂度为O(1)O(1)

/**
 * @param {string[]} words
 * @return {number}
 */
var maxProduct = function(words) {
    
    let hashmap = new Array(26)
    for(let i = 0; i < words.length; i++){
        hashmap[i] = []
        for(let char of words[i]){
            hashmap[i][char.charCodeAt() - "a".charCodeAt()] = true
        }
    }

    let result = 0
    for(let i = 0; i < words.length - 1; i++){
        for(let j = i + 1; j < words.length; j++){
            let k = 0
            for(; k < 26; k++){
                if(hashmap[i][k] && hashmap[j][k]){
                    break
                }
            }

            if(k === 26){
                let size = words[i].length * words[j].length
                result = Math.max(size, result)
            }
        }
    } 
    return result
};

时间复杂度是O(nk+n2)O(nk+n^2),空间复杂度是O(n)O(n)

image.png

继续优化

用一个长度为26的布尔型数组记录字符串中出现的字符。布尔值只有两种可能,即truefalse。这与二进制有些类似,在二进制中数字的每个数位要么是0要么是1。因此,可以将长度为26的布尔型数组用26个二进制的数位代替,二进制的0对应布尔值false,而1对应true。然后通过位运算来比较位数中是否有重复。

上面在判断两个字符串是否包含相同的字符时,可能需要26次布尔运算,通过使用整数的二进制数位来记录字符串中出现的字符,这样可以减少一些计算量。也就是说在判断两个字符串是否包含相同的字符时只需要1次位运算,因此下面解法的时间效率更高。

题解

/**
 * @param {string[]} words
 * @return {number}
 */
var maxProduct = function(words) {
    let hashmap = new Array(words.length).fill(0);
    for(let i = 0; i < words.length; i++){
        for(let char of words[i]){
            hashmap[i] |= 1 << (char.charCodeAt() - "a".charCodeAt());
        }
    }
    let result = 0;
    for(let i = 0; i < words.length - 1; i++){
        for(let j = i + 1; j < words.length; j++){
            if((hashmap[i] & hashmap[j]) === 0){
                let size = words[i].length * words[j].length
                result = Math.max(size, result)
            }
        }
    }
    return result;
};

时间复杂度是O(nk+n2)O(nk+n^2),空间复杂度是O(n)O(n)

image.png

最后,欢迎关注我的专栏,和YK菌做好朋友