题目描述:
给你一个字符串 licensePlate 和一个字符串数组 words ,请你找出并返回 words 中的 最短补全词 。
补全词 是一个包含 licensePlate 中所有的字母的单词。在所有补全词中,最短的那个就是 最短补全词 。
在匹配 licensePlate 中的字母时:
1.忽略 licensePlate 中的 数字和空格 。
2.不区分大小写。
3.如果某个字母在 licensePlate 中出现不止一次,那么该字母在补全词中的出现次数应当一致或者更多。
例如:licensePlate = "aBc 12c",那么它的补全词应当包含字母 'a'、'b' (忽略大写)和两个 'c' 。可能的 补全词 有 "abccdef"、"caaacab" 以及 "cbca" 。
请你找出并返回 words 中的 最短补全词 。题目数据保证一定存在一个最短补全词。当有多个单词都符合最短补全词的匹配条件时取 words 中 最靠前的 那个。
题目分析:
1.需要处理字符串licensePlate,提取出字母,题目要求不区分大小写,所以可以在提取字母的时候把字母全转换成小写字母,可以用tolower函数;
2.在words里面找到所有满足条件的单词,所满足的条件是:单词里面需要有字符串licensePlate所提取出来的所有字母;
3.把满足条件的单词放到一个对象数组p里面,对象的类per内有三个成员变量,分别用来放单词,单词的长度,单词的位置;
4.对数组进行排序,按照长度排序和位置排序,注意:长度排序的优先级大于位置排序;
5.这种遍历字符串的做法,需要注意的一点是:若效字母个数大于1的时候,已经遍历过的字母需要被覆盖掉,使之后面继续遍历是不会被重复计数;比如测试样例1中
字符串licensePlate中被提取出来的有效字母分别是"s p s t",(为了方便理解把第一个s和第二个s区分开,设有效字母是"s1 p s2 t"),但在遍历过程中,s1在碰到step的s时就计数,然后break;s2再碰到step的s时也会计数,那么step的s就会被重复计数;
所以解决方式是当s被计数后,就用"."替换掉;但是我们需要把有效单词放到p里面,单词的字母被改动了那输出就会出现问题,因此,我们可以先把words复制到另一个数组wordss里面,当words里面的单词满足条件,根据位置相同,把相同位置上的wordss里面的单词放到p里面就可以了;
代码:
class per
{
public:
string t;//满足条件的单词
int a;//单词的位置
int mlen;//单词的长度
bool operator<(const per& w ) const{
if(mlen==w.mlen)
{
return a<w.a;
}
return mlen<w.mlen;
}
};
class Solution {
public:
string shortestCompletingWord(string licensePlate, vector<string>& words) {
vector<string> wordss;
vector<char> l;
for(int i=0;i<licensePlate.length();i++)
{
if(isalpha(licensePlate[i]))
{
l.push_back(tolower(licensePlate[i]));
}
}
int y=0;
int l_len=l.size();//有效字母的长度
int words_len=words.size();//单词的个数
for(int i=0;i<words_len;i++)
{
wordss.push_back(words[i]);
}
per p[words_len];
for(int i=0;i<words_len;i++)
{
int str_len=0;//每个单词的长度
str_len=words[i].size();
int count=0;//用来计数字母
if(str_len>=l_len)
{
for(int k=0;k<l_len;k++) {
for (int j = 0; j < str_len; j++) {
if (l[k] == tolower(words[i][j])) {
words[i][j]='.';
count++;
break;
}
}
if(count==l_len)
{
p[y].t=wordss[i];
p[y].a=i;
p[y].mlen=wordss[i].length();
y++;
}
}
}
}
//把p按照单词长度和位置进行排序;
sort(p,p+y);
return p[0].t;
}
};
结果: