哈希表题目:最短补全词

题目

标题和出处

标题:最短补全词

出处:748. 最短补全词

难度

3 级

题目描述

要求

给定一个字符串 licensePlate \texttt{licensePlate} licensePlate 和一个字符串数组 words \texttt{words} words,返回 words \texttt{words} words 中的最短补全词

一个补全词包含 licensePlate \texttt{licensePlate} licensePlate 中所有字母的单词忽略 licensePlate \texttt{licensePlate} licensePlate 中的数字和空格,字母不区分大小写。如果一个字母在 licensePlate \texttt{licensePlate} licensePlate 中出现超过一次,那么该字母在补全词中的出现次数必须相同或更多。

例如, licensePlate   =   "aBc   12c" \texttt{licensePlate = "aBc 12c"} licensePlate = "aBc 12c",它包含字母 ‘a’ \texttt{`a'} ‘a’ ‘b’ \texttt{`b'} ‘b’(忽略大小写)和两个 ‘c’ \texttt{`c'} ‘c’。可能的补全词 "abccdef" \texttt{"abccdef"} "abccdef" "caaacab" \texttt{"caaacab"} "caaacab" "cbca" \texttt{"cbca"} "cbca"

返回 words \texttt{words} words 中的最短补全词。保证答案存在。如果有多个最短补全词,返回在 words \texttt{words} words 中出现的第一个

示例

示例 1:

输入: licensePlate   =   "1s3   PSt",   words   =   ["step",   "steps",   "stripe",   "stepple"] \texttt{licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"]} licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"]
输出: "steps" \texttt{"steps"} "steps"
解释: licensePlate \texttt{licensePlate} licensePlate 包含字母 ‘s’ \texttt{`s'} ‘s’ ‘p’ \texttt{`p'} ‘p’ ‘s’ \texttt{`s'} ‘s’(忽略大小写)和 ‘t’ \texttt{`t'} ‘t’
"step" \texttt{"step"} "step" 包含 ‘t’ \texttt{`t'} ‘t’ ‘p’ \texttt{`p'} ‘p’,但只包含一个 ‘s’ \texttt{`s'} ‘s’
"steps" \texttt{"steps"} "steps" 包含 ‘t’ \texttt{`t'} ‘t’ ‘p’ \texttt{`p'} ‘p’ 和两个 ‘s’ \texttt{`s'} ‘s’
"stripe" \texttt{"stripe"} "stripe" 缺一个 ‘s’ \texttt{`s'} ‘s’
"stepple" \texttt{"stepple"} "stepple" 缺一个 ‘s’ \texttt{`s'} ‘s’
由于 "steps" \texttt{"steps"} "steps" 是唯一包含所有字母的单词,因此是答案。

示例 2:

输入: licensePlate   =   "1s3   456",   words   =   ["looks",   "pest",   "stew",   "show"] \texttt{licensePlate = "1s3 456", words = ["looks", "pest", "stew", "show"]} licensePlate = "1s3 456", words = ["looks", "pest", "stew", "show"]
输出: "pest" \texttt{"pest"} "pest"
解释: licensePlate \texttt{licensePlate} licensePlate 只包含字母 ‘s’ \texttt{`s'} ‘s’。所有的单词都包含 ‘s’ \texttt{`s'} ‘s’,但是 "pest" \texttt{"pest"} "pest" "stew" \texttt{"stew"} "stew" "show" \texttt{"show"} "show" 是最短的。答案是 "pest" \texttt{"pest"} "pest" 因为它是 3 \texttt{3} 3 个单词中最早出现的。

示例 3:

输入: licensePlate   =   "Ah71752",   words   =   ["suggest","letter","of","husband","easy","education","drug","prevent","writer","old"] \texttt{licensePlate = "Ah71752", words = ["suggest","letter","of","husband","easy","education","drug","prevent","writer","old"]} licensePlate = "Ah71752", words = ["suggest","letter","of","husband","easy","education","drug","prevent","writer","old"]
输出: "husband" \texttt{"husband"} "husband"

示例 4:

输入: licensePlate   =   "OgEu755",   words   =   ["enough","these","play","wide","wonder","box","arrive","money","tax","thus"] \texttt{licensePlate = "OgEu755", words = ["enough","these","play","wide","wonder","box","arrive","money","tax","thus"]} licensePlate = "OgEu755", words = ["enough","these","play","wide","wonder","box","arrive","money","tax","thus"]
输出: "enough" \texttt{"enough"} "enough"

示例 5:

输入: licensePlate   =   "iMSlpe4",   words   =   ["claim","consumer","student","camera","public","never","wonder","simple","thought","use"] \texttt{licensePlate = "iMSlpe4", words = ["claim","consumer","student","camera","public","never","wonder","simple","thought","use"]} licensePlate = "iMSlpe4", words = ["claim","consumer","student","camera","public","never","wonder","simple","thought","use"]
输出: "simple" \texttt{"simple"} "simple"

数据范围

  • 1 ≤ licensePlate.length ≤ 7 \texttt{1} \le \texttt{licensePlate.length} \le \texttt{7} 1licensePlate.length7
  • licensePlate \texttt{licensePlate} licensePlate 由数字、大小写字母或空格 ‘   ’ \texttt{` '} ‘ ’ 组成
  • 1 ≤ words.length ≤ 1000 \texttt{1} \le \texttt{words.length} \le \texttt{1000} 1words.length1000
  • 1 ≤ words[i].length ≤ 15 \texttt{1} \le \texttt{words[i].length} \le \texttt{15} 1words[i].length15
  • words[i] \texttt{words[i]} words[i] 由小写英语字母组成

解法

思路和算法

补全词满足在忽略大小写的情况下,单词中的每个字母的出现次数都大于等于 licensePlate \textit{licensePlate} licensePlate 中的相同字母的出现次数。为了判断数组 words \textit{words} words 中的每个单词是否是补全词,需要对 licensePlate \textit{licensePlate} licensePlate 和数组 words \textit{words} words 中的每个单词分别统计每个字母的出现次数,然后分别将数组 words \textit{words} words 中的每个单词和 licensePlate \textit{licensePlate} licensePlate 比较每个字母的出现次数。

由于 licensePlate \textit{licensePlate} licensePlate 可能包含大写和小写字母,以及数字和空格,因此在统计每个字母的出现次数时,应跳过非字母的字符,对于字母字符需要转成小写字母再统计出现次数。

对于数组 words \textit{words} words 中的单词,由于只包含小写字母,因此可以直接对每个单词遍历,统计每个字母的出现次数。

统计每个字母的出现次数之后,判断一个单词是否是补全词的方法为:对于每个小写字母,比较字母在该单词中的出现次数和在 licensePlate \textit{licensePlate} licensePlate 中的出现次数,只要有一个字母在单词中的出现次数小于在 licensePlate \textit{licensePlate} licensePlate 中的出现次数,则该单词不是补全词,如果所有字母在单词中的出现次数都大于或等于在 licensePlate \textit{licensePlate} licensePlate 中的出现次数,则该单词是补全词。

由于题目要求返回数组 words \textit{words} words 中的最短补全词,因此需要在遍历单词的过程中记录最短补全词。初始时,最短补全词为空。从左到右遍历数组 words \textit{words} words,对于每个单词,如果该单词是补全词且该单词的长度小于最短补全词的长度(或者最短补全词为空),则将该单词赋给最短补全词。

实现方面,有两点可以优化。

  • 由于只需要考虑小写字母,因此可以使用长度为 26 26 26 的数组记录每个字母的出现次数。

  • 如果一个单词的长度大于或等于最短补全词的长度且最短补全词不为空,则该单词即使是补全词也不可能是最短补全词,因此可以跳过该单词。按照该优化思路,可以先比较单词长度和最短补全词的长度,如果单词长度小于最短补全词的长度再判断单词是不是补全词。

代码

class Solution {
    public String shortestCompletingWord(String licensePlate, String[] words) {
        int[] licenseCounts = new int[26];
        int licenseLength = licensePlate.length();
        for (int i = 0; i < licenseLength; i++) {
            char c = licensePlate.charAt(i);
            if (Character.isLetter(c)) {
                licenseCounts[Character.toLowerCase(c) - 'a']++;
            }
        }
        String completingWord = "";
        int wordsCount = words.length;
        for (int i = 0; i < wordsCount; i++) {
            String word = words[i];
            if (completingWord.length() > 0 && word.length() >= completingWord.length()) {
                continue;
            }
            int[] wordCounts = new int[26];
            int wordLength = word.length();
            for (int j = 0; j < wordLength; j++) {
                char c = word.charAt(j);
                wordCounts[c - 'a']++;
            }
            boolean isCompleting = true;
            for (int j = 0; j < 26; j++) {
                if (wordCounts[j] < licenseCounts[j]) {
                    isCompleting = false;
                    break;
                }
            }
            if (isCompleting) {
                completingWord = word;
            }
        }
        return completingWord;
    }
}

复杂度分析

  • 时间复杂度: O ( l p + n ( l + ∣ Σ ∣ ) ) O(l_p + n(l + |\Sigma|)) O(lp+n(l+∣Σ∣)),其中 n n n 是数组 words \textit{words} words 的长度, l p l_p lp 是字符串 licensePlate \textit{licensePlate} licensePlate 的长度, l l l 是数组 words \textit{words} words 中的单词的平均长度, Σ \Sigma Σ 是字符集,这道题中 Σ \Sigma Σ 是全部小写英语字母, ∣ Σ ∣ = 26 |\Sigma| = 26 ∣Σ∣=26。需要遍历 licensePlate \textit{licensePlate} licensePlate 统计每个字母的出现次数,对于数组 words \textit{words} words 中的每个单词,需要遍历单词统计每个字母的出现次数并根据每个字母的出现次数判断该单词是否是补全词。

  • 空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(∣Σ∣),其中 Σ \Sigma Σ 是字符集,这道题中 Σ \Sigma Σ 是全部小写英语字母, ∣ Σ ∣ = 26 |\Sigma| = 26 ∣Σ∣=26。空间复杂度主要取决于哈希表,需要使用哈希表记录每个字母的出现次数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值