[路飞]_318.最大单词长度乘积

227 阅读2分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

318. 最大单词长度乘积

题目

给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0 。

示例1

输入:words = ["abcw","baz","foo","bar","xtfn","abcdef"]
输出:16 
解释:这两个单词为 "abcw", "xtfn"

示例2

输入: words = ["a","ab","abc","d","cd","bcd","abcd"]
输出: 4 
解释 : 这两个单词为 "ab", "cd"

示例3

输入: words = ["a","aa","aaa","aaaa"]
输出: 0 
解释 : 不存在这样的两个单词。

提示

  • 2 <= words.length <= 1000
  • 2 <= words[i].length <= 1000
  • words[i] 仅包含小写字母

题解

状态压缩 + 位运算

一看数据量,2 <= words.length <= 1000,这不是可以用 forfor 循环吗,貌似寻找两个字符串乘积最大值且两个单词不含有公共字母也必须枚举所有两个字符串组合。

假设枚举所有字符串组合,words.lengthwords.length 长度最长是 10001000 , words[i]words[i] 长度最长是 10001000,这个用例大概要消耗 (1000100010001000)(1000 * 1000 * 1000 * 1000) ,这个数量级肯定是要超时的。

如何才能降低时间复杂度呢?

分析上述数据,这里寻找两个字符串时间复杂度是 O(n2)O(n^2) ,两个单词是否含有公共字母时间复杂度是 O(n2)O(n^2),如果可以将两个单词是否含有公共字母时间复杂度降低到O(1),是不是就降低了总体的时间复杂度?

如何快速判定两个长度为 2 <= words[i].length <= 1000 的字符串不含有公共字母;这里就用到了状态压缩。

任意长度的字符串 words[i] 仅包含小写字母,所以可以使用26位二进制表示words[i] 

  • 当二进制位位0,表示words[i] 字符串中不存在该字符串
  • 当二进制位位1,表示words[i] 字符串中存在该字符串

字符串转二进制的优点是可以快速判断两个字符串是否含有公共字母;
对于任意二进制,如果 101&010===0101 \& 010 === 0 表示这两个二进制数在相同位置上没有同时为1
通过二进制这一个运算,可以快速判定两个字符串是否含有公共字母

根据上述思路编辑代码如下:

代码

var maxProduct = function(words) {
    const len = words.length;
    let list = Array(len).fill(0)
    function stringToNumber (s){
        return s.charCodeAt() - 'a'.charCodeAt()
    }
    for(let i = 0  ; i < len ; i++){
        const s = words[i];
        for(let j = 0 ; j < s.length ; j++){
            list[i] |= 1 << (stringToNumber(s[j]))
        }
    }
    let result = 0;
    for(let i = 0 ; i < len ; i++){
        for(let j = i+1 ; j < len ; j++){
            if((list[i] & list[j]) == 0){
               
               result = Math.max(result,words[i].length * words[j].length) 
            }
        }
    }
    return result

};