单词长度的最大乘积

175 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

前言

笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。

系列文章收录《算法》专栏中。

力扣题目链接

问题描述

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

剖析

暴力破解

看的这个题的第一反应是很简单,直接暴力破解,时间复杂度为O(n^2*m)(n为数组长度,m为平均单词长度),空间复杂度为O(1),比较简单也能通过.

/**
 * 字符串数组每个字符串和其他字符串进行比较,如果不同就把两个字符串长度进行乘积,最后只保留乘积较大数。
 * 每个字符串怎么比较?把字符串的每个字符抽出来看另一个字符串是否contain
 *
 * @param words
 * @return
 */
public static int maxProduct(String[] words) {
    if (words.length < 2 || words.length > 1000) {
        throw new RuntimeException("字符串数组长度不符合规定长度");
    }

    int maxProduct = 0;
    for (int i = 0; i < words.length; i++) {
        for (int j = i + 1; j < words.length; j++) {
            String a = words[i];
            String b = words[j];

            if (a.length() <= 0 || a.length() > 1000 || b.length() <= 0 || b.length() > 1000) {
                throw new RuntimeException("字符串长度不符合规定长度");
            }

            boolean bContainA = false;
            for (char ac : a.toCharArray()) {
                if (b.contains(String.valueOf(ac))) {
                    bContainA = true;
                    break;
                }
            }

            if (!bContainA) {
                int tempProduct = a.length() * b.length();
                maxProduct = (maxProduct < tempProduct) ? tempProduct : maxProduct;
            }
        }
    }
    return maxProduct;
}

使用位图

做算法题的时候还需要注意一些特殊的关键信息,比如这里的“假设字符串中只包含英语的小写字母。”,

  • 只有26个字母,那么我是不是可以使用一个Integer中的每个位代表这个字符串中出现的小写字母?如果只有16个的话那当然可以使用Short。就算字符串中的字母重复了也没关系不会重复占用位。
  • 除了使用位表示小写字母之外,我们还需要记录字符串长度,方便后面得出长度乘积。
  • 有了位图,直接使用与运算如果等于0肯定就说明不存在重复字符。

那么我们来算下复杂度:

  • 时间复杂度为O(n*m+n^2)
  • 空间复杂度为O(n)

下面我们直接看代码

代码

public static int maxProduct(String[] words) {
    int result = 0;
    Map<Integer, Integer> bitMap = new HashMap<>((int) (words.length / 0.75f));
    for (String word : words) {
        int bit = 0;
        for (char c : word.toCharArray()) {
            bit |= (1 << (c - 'a'));
        }
        bitMap.put(bit, Math.max(bitMap.getOrDefault(bit, 0), word.length()));
    }

    Set<Integer> keySet = bitMap.keySet();
    List<Integer> keyList = new ArrayList<>(keySet);
    for (int i = 0; i < keyList.size(); i++) {
        for (int j = i + 1; j < keyList.size(); j++) {
            if ((keyList.get(i) & keyList.get(j)) == 0) {
                result = Math.max(result, bitMap.get(keyList.get(i)) * bitMap.get(keyList.get(j)));
            }
        }
    }
    return result;
}