2021.11.17LeetCode每日一题——最大单词长度乘积

目录

最大单词长度乘积

描述

示例 1

示例 2

示例 3

提示

方法一:位运算

方法二:位运算优化


最大单词长度乘积

描述

给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。

你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。

示例 1

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

示例 2

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

示例 3

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

提示

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

方法一:位运算

为了得到最大单词长度乘积,朴素的做法是,遍历字符串数组 \textit{words}words 中的每一对单词,判断这一对单词是否有公共字母,如果没有公共字母,则用这一对单词的长度乘积更新最大单词长度乘积。

用 n 表示数组 words 的长度,用 l_i 表示单词 words[i] 的长度,其中 0≤i<n,则上述做法需要遍历字符串数组 words 中的每一对单词,对于下标为 i 和 j 的单词,其中 i<j,需要O(l_i \times l_j)的时间判断是否有公共字母和计算长度乘积。因此上述做法的时间复杂度是

O(\sum_{0\le i<j<n}l_i \times l_j)

该时间复杂度高于 O(n^2)

如果可以将判断两个单词是否有公共字母的时间复杂度降低到 O(1),则可以将总时间复杂度降低到 O(n^2)

可以使用位运算预处理每个单词,通过位运算操作判断两个单词是否有公共字母。由于单词只包含小写字母,共有 26 个小写字母,因此可以使用位掩码的最低 26 位分别表示每个字母是否在这个单词中出现。将 a 到 z 分别记为第 0 个字母到第 25 个字母,则位掩码的从低到高的第 i 位是 1 当且仅当第 i 个字母在这个单词中,其中 0≤i≤25。

用数组 masks 记录每个单词的位掩码表示。计算数组 masks 之后,判断第 i 个单词和第 j 个单词是否有公共字母可以通过判断 masks[i] & masks[j] 是否等于 0 实现,当且仅当 masks[i] & masks[j]=0 时第 i 个单词和第 j 个单词没有公共字母,此时使用这两个单词的长度乘积更新最大单词长度乘积。

class Solution {
    public int maxProduct(String[] words) {
        int len=words.length;
        int[] masks=new int[len];
        for (int i=0;i<len;i++){
            String word=words[i];
            for (int j = 0; j < word.length(); j++) {
                masks[i] |= 1<<(word.charAt(j)-'a');//将出现的字符按照a-z:0-25的映射规则,对低26位二进制数的对应位数进行置1,将结果与原来的值进行按位或运算
            }
        }
        int max=0;
        for (int i = 0; i < len; i++) {
            for (int j = i+1; j < len; j++) {
                if ((masks[i]&masks[j])==0){//如果两个单词不重复(要求二者没有在某个位上同时为1,即二者没有公共字母)
                    max=Math.max(max,words[i].length()*words[j].length());//取乘积较大值
                }
            }
        }
        return max;
    }
}

方法二:位运算优化

从上述过程中我们可以发现,单词"meet"和单词"met"的mask掩码值相等,因为他们都包含了字母"m"、"e"、"t",所以此时我们只需要保留较大的长度单词"meet"即可。这个操作可以用哈希表实现。

我们可以使用哈希表记录每个位掩码对应的最大单词长度,然后遍历哈希表中的每一对位掩码,如果这一对位掩码的按位与运算等于 0,则用这一对位掩码对应的长度乘积更新最大单词长度乘积。

由于每个单词的位掩码都不等于 0,任何一个不等于 0 的数和自身做按位与运算的结果一定不等于 0,因此当一对位掩码的按位与运算等于 0 时,这两个位掩码一定是不同的,对应的单词也一定是不同的。

class Solution {
    public int maxProduct(String[] words) {
        int len=words.length;
        HashMap<Integer,Integer> map=new HashMap<>();//存储每一个掩码对应的最大字符长度
        for (int i=0;i<len;i++){
            int mask=0;
            String word=words[i];
            for (int j = 0; j < word.length(); j++) {
                mask |= 1<<(word.charAt(j)-'a');//将出现的字符按照a-z:0-25的映射规则,对低26位二进制数的对应位数进行置1,将结果与原来的值进行按位或运算
            }
            if (word.length()>map.getOrDefault(mask,0)){//如果当前掩码值的最大长度比当前长度小
                map.put(mask,word.length());//则更新最大长度
            }
        }
        int max=0;
        for (Integer mask1 : map.keySet()) {
            for (Integer mask2 : map.keySet()) {
                if ((mask1&mask2)==0){//如果两个单词不重复
                    max=Math.max(max,map.get(mask1)*map.get(mask2));//取乘积较大值
                }
            }
        }
        return max;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/121372299