318. 最大单词长度乘积【我亦无他唯手熟尔】

简介: 318. 最大单词长度乘积【我亦无他唯手熟尔】

318. 最大单词长度乘积

难度中等

给定一个字符串数组 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] 仅包含小写字母

题解

思路:遍历
每次找最长的单词,然后找到符合题意(即与它没有公共字母)的另外一个单词,计算length(word[i]) * length(word[j]) ,并和记录的最大比较
就找下一个最长的单词,然后找到它所对应的符合题意的另外一个单词,计算length(word[i]) * length(word[j]) ,并和记录的最大比较
依次,知道完为止
可以先对字符串数组从长到短排个序
class Solution {
    public int maxProduct(String[] words) {
         Arrays.sort(words,new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return -(o1.length()-o2.length());
            }
        });
        int maxProduct=0;
//        System.out.println(Arrays.toString(words));
        for (int i=0;i<words.length;i++){
            for (int j = i+1; j <words.length; j++) {
                HashSet<Character> set=new HashSet<>();
                int m=words[i].length();
                int n=words[j].length();
                //有公共字母==两个的并集大小<两个大小之和
                //没有的话是等于
                for (int k = 0; k < m; k++) {
                    set.add(words[i].charAt(k));
                }
                for (int k = 0; k < n; k++) {
                    set.add(words[j].charAt(k));
                }
//                System.out.println(set);
                if (set.size()==m+n){
                    if(m*n>maxProduct){
                        maxProduct=m*n;
                    }
                }
            }
        }
        return maxProduct;
    }
}


错误原因没有考虑到单词本身会有重复字母

排序没作用

class Solution {
    public int maxProduct(String[] words) {
        int maxProduct=0;
//        System.out.println(Arrays.toString(words));
        for (int i=0;i<words.length;i++){
            for (int j = i+1; j <words.length; j++) {
                boolean notHasCommonChar=true;
                int m=words[i].length();
                int n=words[j].length();
                for (int k = 0; k <m; k++) {
                    char ch=words[i].charAt(k);
                    int index = words[j].indexOf(ch);
                    if (index!=-1){
                        notHasCommonChar=false;
                        break;
                    }
                }
                if (notHasCommonChar){
                    if(m*n>maxProduct){
                        maxProduct=m*n;
                    }
                }
            }
        }
        return maxProduct;
    }
}



官方

方法一:位运算
如果可以将判断两个单词是否有公共字母的时间复杂度降低到 O(1),则可以将总时间复杂度降低到 O(n2)


)。可以使用位运算预处理每个单词,通过位运算操作判断两个单词是否有公共字母。由于单词只包含小写字母,共有 26 个小写字母,因此可以使用位掩码的最低 26 位分别表示每个字母是否在这个单词中出现。将a 到 z 分别记为第 0 个字母到第 25 个字母,则位掩码的从低到高的第 i 位是 11 当且仅当第 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 length = words.length;
        int[] masks = new int[length];
        for (int i = 0; i < length; i++) {
            String word = words[i];
            int wordLength = word.length();
            for (int j = 0; j < wordLength; j++) {
                masks[i] |= 1 << (word.charAt(j) - 'a');
            }
        }
        int maxProd = 0;
        for (int i = 0; i < length; i++) {
            for (int j = i + 1; j < length; j++) {
                if ((masks[i] & masks[j]) == 0) {
                    maxProd = Math.max(maxProd, words[i].length() * words[j].length());
                }
            }
        }
        return maxProd;
    }
}

作者:LeetCode-Solution

链接:https://leetcode-cn.com/problems/maximum-product-of-word-lengths/solution/zui-da-dan-ci-chang-du-cheng-ji-by-leetc-lym9/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析
时间复杂度:O(L+n2 ),其中 L 是数组words 中的全部单词长度之和,n 是数组 words 的长度。预处理每个单词的位掩码需要遍历全部单词的全部字母,时间复杂度是 O(L),然后需要使用两重循环遍历位掩码数组masks 计算最大单词长度乘积,时间复杂度是 O(n2),因此总时间复杂度是 O(L + n2)


空间复杂度:O(n),其中 n 是数组 words 的长度。需要创建长度为 n 的位掩码数组 masks。




方法二:位运算优化

https://leetcode-cn.com/problems/maximum-product-of-word-lengths/solution/zui-da-dan-ci-chang-du-cheng-ji-by-leetc-lym9/

class Solution {
    public int maxProduct(String[] words) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int length = words.length;
        for (int i = 0; i < length; i++) {
            int mask = 0;
            String word = words[i];
            int wordLength = word.length();
            for (int j = 0; j < wordLength; j++) {
                mask |= 1 << (word.charAt(j) - 'a');
            }
            if (wordLength > map.getOrDefault(mask, 0)) {
                map.put(mask, wordLength);
            }
        }
        int maxProd = 0;
        Set<Integer> maskSet = map.keySet();
        for (int mask1 : maskSet) {
            int wordLength1 = map.get(mask1);
            for (int mask2 : maskSet) {
                if ((mask1 & mask2) == 0) {
                    int wordLength2 = map.get(mask2);
                    maxProd = Math.max(maxProd, wordLength1 * wordLength2);
                }
            }
        }
        return maxProd;
    }
}
相关文章
|
机器学习/深度学习 算法
1816. 截断句子【我亦无他唯手熟尔】
1816. 截断句子【我亦无他唯手熟尔】
77 0
1446. 连续字符【我亦无他唯手熟尔】
1446. 连续字符【我亦无他唯手熟尔】
54 0
869. 重新排序得到 2 的幂【我亦无他唯手熟尔】
869. 重新排序得到 2 的幂【我亦无他唯手熟尔】
48 0
|
7月前
|
算法
LeetCode题:581. 最短无序连续子数组,242. 有效的字母异位词,202. 快乐数
LeetCode题:581. 最短无序连续子数组,242. 有效的字母异位词,202. 快乐数
56 0
|
存储 算法
【LeetCode】每日一题&最后一个单词的长度&投票法求解多数元素&异或操作符巧解只出现一次的数字&整数反转
【LeetCode】每日一题&最后一个单词的长度&投票法求解多数元素&异或操作符巧解只出现一次的数字&整数反转
|
7月前
【每日一题Day226】L1156单字符重复子串的最大长度 | 贪心+滑动窗口
【每日一题Day226】L1156单字符重复子串的最大长度 | 贪心+滑动窗口
61 0
【每日挠头算法题(8)】最后一个单词的长度|重新排列字符串
【每日挠头算法题(8)】最后一个单词的长度|重新排列字符串
【每日挠头算法(4)】字符串相加|字符串相乘
【每日挠头算法(4)】字符串相加|字符串相乘
|
算法
【算法挨揍日记】day05——209. 长度最小的子数组、3. 无重复字符的最长子串
题目描述: 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
362 0
1218. 最长定差子序列【我亦无他唯手熟尔】
1218. 最长定差子序列【我亦无他唯手熟尔】
46 0