题目描述:
如果单词列表(words
)中的一个单词包含牌照(licensePlate
)中所有的字母,那么我们称之为完整词。在所有完整词中,最短的单词我们称之为最短完整词。
单词在匹配牌照中的字母时不区分大小写,比如牌照中的 "P"
依然可以匹配单词中的 "p"
字母。
我们保证一定存在一个最短完整词。当有多个单词都符合最短完整词的匹配条件时取单词列表中最靠前的一个。
牌照中可能包含多个相同的字符,比如说:对于牌照 "PP"
,单词 "pair"
无法匹配,但是 "supper"
可以匹配。
示例 1:
输入:licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"] 输出:"steps" 说明:最短完整词应该包括 "s"、"p"、"s" 以及 "t"。对于 "step" 它只包含一个 "s" 所以它不符合条件。同时在匹配过程中我们忽略牌照中的大小写。
示例 2:
输入:licensePlate = "1s3 456", words = ["looks", "pest", "stew", "show"] 输出:"pest" 说明:存在 3 个包含字母 "s" 且有着最短长度的完整词,但我们返回最先出现的完整词。
注意:
- 牌照
(licensePlate)
的长度在区域[1, 7]
中。 - 牌照
(licensePlate)
将会包含数字、空格、或者字母(大写和小写)。 - 单词列表
(words)
长度在区间[10, 1000]
中。 - 每一个单词
words[i]
都是小写,并且长度在区间[1, 15]
中。
想法1:把licensePlate中的字母挑出来,存放到一个list中,然后遍历words中的每一个字符串,判断list中是否包含该字符;若存在该字符,则将该字符从list中移除。当list中为空时,就说明licensePlate在words中匹配了。同时还需要设置一个min变量和result变量,min用来记录当前最短字符串的长度,result用来记录对应的最短字符串。
上代码:
class Solution {
public String shortestCompletingWord(String licensePlate, String[] words){
licensePlate = licensePlate.toLowerCase();
ArrayList<Character> list = pick(licensePlate);
//用来记录最短单词长度的min和最短单词的result
int min = Integer.MAX_VALUE;
String result = null;
for (int i = 0; i < words.length; i++) {
//先将list复制一份,对备份进行操作
ArrayList<Character> temp = (ArrayList<Character>) list.clone();
int len = words[i].length();
for (int j = 0; j < len; j++) {
//list中含有words[i]中的字符,将该字符移除
//此处有一个陷阱,如果是这样写的话:temp.remove(words[i].charAt(j));
//很可能报数组越界的错误,因为list会调用这个remove(int index)方法;
//自动把char类型转为int类型,而我们想要的是remove(Object o)
//所以应该传入的参数应该是new Character(words[i].charAt(j))
if(temp.contains(words[i].charAt(j))){
temp.remove(new Character(words[i].charAt(j)));
}
if(temp.size() == 0 && len < min){
min = len;
result = words[i];
}
}
}
return result;
}
/**
* 将licensePlate中的字母挑出来
* 并存放到list中
*/
public ArrayList<Character> pick(String S){
ArrayList<Character> list = new ArrayList<>();
for (int i = 0; i < S.length(); i++) {
if(S.charAt(i) >= 'a' && S.charAt(i) <= 'z'){
list.add(S.charAt(i));
}
}
return list;
}
}
上面代码中提到的坑,只怪自己太菜,还debug了半天。。。。。 ╮(╯▽╰)╭
想法2:同样的先把licensePlate中的字母先挑出来,但是不把对应的字符记录下来,而是用一个长度为26的int型数组来存放每个字母在licensePlate中出现的次数。同样在遍历words中的每一个字符串时,也用一个长度为26的int型数组来存放每个字母出现的次数。然后将这两个数字进行比较,第一个数组中的26个值都小于或等于第二个数组中的26个值即可。同时用一个变量index来记录当前最短字符的下标。
上代码:
class Solution {
public String shortestCompletingWord(String licensePlate, String[] words) {
int[] count = new int[26];
//统计licensePlate中每个字母出现的次数
for (int i = 0; i < licensePlate.length(); i++) {
String lp = licensePlate.toLowerCase();
char c = lp.charAt(i);
if (Character.isLetter(c)) {
count[c - 'a']++;
}
}
int index = Integer.MAX_VALUE;//记录最短字符串的下标
int k = 0;
for (String word : words) {
int[] count2 = new int[26];
//统计字符串word中每个字母出现的次数
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
count2[c - 'a']++;
}
boolean flag = true;
//第二个数组中只要有一个数字小于第一个数组中的值,
//就表明当前的word不匹配licensePlate
for (int t = 0; t < 26; t++) {
if (count[t] != 0 && count2[t] < count[t]) {
flag = false;
break;
}
}
//更新index的值
if (flag == true) {
if (index == Integer.MAX_VALUE)
index = k;
else if (word.length() < words[index].length())
index = k;
}
k++;
}
return words[index];
}
}
以上两种方法的运行时间都差不多,没有太大差别。
最后,各位路过的大佬,如果你有更好的方法,欢迎指导,谢谢哦!