使用位运算求解单词长度的最大乘积

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

题目描述

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

解法

保存每一个字符串的掩码

  • 由于字符串中只包含小写字母,这个字符串中最多只能包含26种不同字母吗,所以我们可以使用26位掩码来表示这个字符串中包含的字符,规定字符’a’对应第0位,字符’z’对应第25位。
  • 对于两个完全不同字符串(非空串,对应掩码非0)对应的的掩码mask1和mask2,mask1 & mask2的结果一定为0,如果包含相同的字符,结果一定非0。
  • 根据这个规则,我们可以化简字符串比较的时间复杂度,这样总体的时间复杂度只是嵌套循环取两字字符串,即O(n2)。
    在这里插入图片描述
  • 算法描述:
    • 将每一个字符串转换为掩码后保存,遍历每一对掩码进行按位与的操作,找出按位与结果为0的长度乘积最大值。
// 计算当前字符与'a'的距离,即当前字符在掩码中的位置
function getLetterIndex (s) {
    return s.charCodeAt(0) - 'a'.charCodeAt(0)
}

// 将字符串转化为掩码
function transferWord2Bit (word) {
    let result = 0
    for(let i = 0; i < word.length; i++) {
        result |= 1 << getLetterIndex(word.charAt(i))
    }
    return result
}

var maxProduct = function(words) {
    const wordsBits = words.map(word => transferWord2Bit(word))
    let max = 0
    for(let i = 0; i < wordsBits.length; i++) {
        for(let j = i + 1; j < wordsBits.length; j++){
            if (!(wordsBits[i] & wordsBits[j])) {
                max = Math.max(max, words[i].length * words[j].length)
            }
        }
    }
    return max
};

记录掩码和最长字符串长度的映射

  • 按上面的思路,我们把字符串转化为掩码,保存了所有字符串的掩码。但是,对于abcd和aabbccdd来说,它们的掩码都是00000000000000000000001111,我们只需要aabbccdd,对于abcd,会进行相同的掩码比较且因为它比较短而被舍弃,这一点是可以先前预料并且进行改进的。
  • 改进:可以只保存 { 掩码: 字符串最长长度 },掩码的数量是 <= 字符串数量的,所以时间复杂度比O(n2)小。
    • 保存Map{ 掩码: 字符串最长长度 }:
      • 计算每一个字符串的掩码。
      • 如果map中没有该掩码,就存储该字符串长度;
      • 如果有且该字符串长度大于map中掩码对应长度,就更新。
      const maskMap = new Map() // key-value: wordMask-maxLength
      words.map(word => {
          const wordLength = word.length
          const wordMask = transferWord2Bit(word)
          if (wordLength > (maskMap.get(wordMask) || 0)) {
              maskMap.set(wordMask, wordLength)
          }
      })
      
    • 遍历map中所有的掩码map.keys(),如果一对掩码完全不同,则计算其长度对乘积并更新结果值。
      const wordMasks = Array.from(maskMap.keys())
      for(const mask1 of wordMasks) {
          const maxWord1Length = maskMap.get(mask1)
          for(const mask2 of wordMasks) {
              const maxWord2Length = maskMap.get(mask2)
              if (!(mask2 & mask1)) {
                  max = Math.max(max, maxWord1Length * maxWord2Length)
              }
          }
      }
      	```
      
// getLetterIndex 和 transferWord2Bit同上
var maxProduct = function(words) {
   const maskMap = new Map() // key-value: wordMask-maxLength
   words.map(word => {
       const wordLength = word.length
       const wordMask = transferWord2Bit(word)
       if (wordLength > (maskMap.get(wordMask) || 0)) {
           maskMap.set(wordMask, wordLength)
       }
   })


   let max = 0
   const wordMasks = Array.from(maskMap.keys())
   for(const mask1 of wordMasks) {
       const maxWord1Length = maskMap.get(mask1)
       for(const mask2 of wordMasks) {
           const maxWord2Length = maskMap.get(mask2)
           if (!(mask2 & mask1)) {
               max = Math.max(max, maxWord1Length * maxWord2Length)
           }
       }
   }
   
   return max
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值