单词长度的最大乘积


题目描述:

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

输入: words = ["abcw","baz","foo","bar","fxyz","abcdef"]
输出: 16 
解释: 这两个单词为 "abcw", "fxyz"。它们不包含相同字符,且长度的乘积最大。

数据集:

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

思路分析

首先会想到的是, 通过两重for循环,逐个比较两个字符串是否有相同字符,如果没有相同字符,则计算他们长度的乘积, 然后与当前最大值比较,如果大于最大值,则将当前乘积结果更新为最大值。但这种算法逻辑时间复杂度较高,光是两重for循环就是O(n*n),同时还有两个字符串进行比较的时间消耗,时间复杂度势必大于O(n2)。然后细读题目,其中说到只包含小写字母,沿着这个提示,这一下子可以将字符串比较的难度降低了,我们想到了通过位运算的方式来降低比较带来的时间消耗,位运算的时间复杂度可以做到O(1),所以这样操作后,整体的时间复杂度就只有O(n2)。
初始化一个长度为26的数组: [0][0][0][0][0][0][0][0][0][0][0][0]0][0][0][0][0][0]0][0][0][0][0][0][0][0],低位从a开始到z,每个字符与‘a’进行比较后,找到对应的数组位置,比如‘c’,那么下标为‘c’ - ‘a’ = 2,我们通过将1左移的方式,移动两位, 及 1<<2,此时数组变成[0][0][0][0][0][0][0][0][0][0][0][0]0][0][0][0][0][0]0][0][0][0][0][1][0][0], 然后将移动后的值与最初的值进行或运算,这样一个字符串中的字符在数组对应的位置都会被更新为1, 然后我们将两个字符串进行比较的时候,再将位运算得到的结果进行一次与操作,如果结果为0,则说明两个字符串中不存在相同的字符

代码如下

func maxProduct(words []string) int {
    length := len(words)
    mask := make([]int, length)
    for i := 0; i < length; i++ {
        mask = append(mask, 0)
    }
    for i := 0; i < length; i++ {
        curWord := words[i]
        for j := 0; j < len(curWord); j++ {
            mask[i] |= 1 << (curWord[j] - 'a')
        }
    }
    result := 0
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            if mask[i] & mask[j] == 0 {
                curMul := len(words[i]) * len(words[j])
                if curMul > result {
                    result = curMul
                }
            }
        }
    }
    return  result
}

进一步的优化

由于本题是求长度的最大值,所以我们没必要把所有的字符串都进行计算,我们可以把相同的位运算结果,只记录最大长度,这样可以减少比较的次数,代码如下

func maxProduct(words []string) int {
        length := len(words)
    /*var mask []int
    for i := 0; i < length; i++ {
        mask = append(mask, 0)
    }*/
    mask := make([]int, length)
    maskMap := make(map[int]int, length)
    for i := 0; i < length; i++ {
        curWord := words[i]
        for j := 0; j < len(curWord); j++ {
            mask[i] |= 1 << (curWord[j] - 'a')
        }
        v, ok := maskMap[mask[i]]
        if !ok  {
            maskMap[mask[i]] = len(curWord)
        }else {
            if len(curWord) > v {
                maskMap[mask[i]] = len(curWord)
            }
        }
    }
    result := 0
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            if mask[i] & mask[j] == 0 {
                vi , _ := maskMap[mask[i]]
                vj , _ := maskMap[mask[j]]
                curMul := vi * vj
                if curMul > result {
                    result = curMul
                }
            }
        }
    }
    return  result
}

总结

通过该题,给我们比较字符串提供了一种新的思路, 就是通过进行位运算进行比较,这种方式的好处就在于时间复杂度位O(1)


文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
评论
  目录