题目
标题和出处
标题:最短补全词
出处: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} 1≤licensePlate.length≤7
- licensePlate \texttt{licensePlate} licensePlate 由数字、大小写字母或空格 ‘ ’ \texttt{` '} ‘ ’ 组成
- 1 ≤ words.length ≤ 1000 \texttt{1} \le \texttt{words.length} \le \texttt{1000} 1≤words.length≤1000
- 1 ≤ words[i].length ≤ 15 \texttt{1} \le \texttt{words[i].length} \le \texttt{15} 1≤words[i].length≤15
- 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。空间复杂度主要取决于哈希表,需要使用哈希表记录每个字母的出现次数。