819.最常见的单词——Leetcode每日一题(2022.4.17)
前言
今天早起A了这道题,昨天先是高铁上打力扣杯,晚上打双周赛,今天早上A完这题有去打周赛,强度拉满了属于是。
不过,yysy,一开始看到题目的时候,嗯,绿色题,看我不秒了它,欸,错了!一定是那里不对,改一下,挖草,又错了!就这样反复几次,终于对了。只能说这道题目看似很简单的题目,但是暗藏的细节还是很多的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WvTa92da-1650204164395)(C:\Users\86152\AppData\Roaming\Typora\typora-user-images\image-20220417194408970.png)]
以下关于题目的来源都来自leetcode官方。
题目描述
给定一个段落 (paragraph) 和一个禁用单词列表 (banned)。返回出现次数最多,同时不在禁用列表中的单词。
题目保证至少有一个词不在禁用列表中,而且答案唯一。
禁用列表中的单词用小写字母表示,不含标点符号。段落中的单词不区分大小写。答案都是小写字母。
示例
输入:
paragraph = "Bob hit a ball, the hit BALL flew far after it was hit."
banned = ["hit"]
输出: "ball"
解释:
"hit" 出现了3次,但它是一个禁用的单词。
"ball" 出现了2次 (同时没有其他单词出现2次),所以它是段落里出现次数最多的,且不在禁用列表中的单词。
注意,所有这些单词在段落里不区分大小写,标点符号需要忽略(即使是紧挨着单词也忽略, 比如 "ball,"),
"hit"不是最终的答案,虽然它出现次数更多,但它在禁用单词列表中。
提示
1 <= 段落长度 <= 1000
0 <= 禁用单词个数 <= 100
1 <= 禁用单词长度 <= 10
答案是唯一的, 且都是小写字母 (即使在 paragraph 里是大写的,即使是一些特定的名词,答案都是小写的。)
paragraph 只包含字母、空格和下列标点符号!?’,;.
不存在没有连字符或者带有连字符的单词。
单词里只包含字母,不会出现省略号或者其他标点符号。
解题思路——哈希表
我觉得看到这种题目,有一定经验都没啥好说的吧,直接哈希表就完事了。
我们可以使用一个哈希表去存储ban的单词,这样我们可以只用o(n)的空间来换取,暴力的查找带来O(m*n)的时间,置需要O(1)的时间复杂即可,显然是很划算的。
注:n为ban数组的长度,而m是段落的长度。、
当然我们在遍历字符串的时候,其实也需要使用一个哈希表来完成单词的计数,我们可以ban的哈希表跳过一些被ban的单词的计数。
最后是一些细节问题,由于规定是小写,所以我们需要把大写转换成小写,然后遍历的时候记录字母,拼成单词,当遇到非字母时则需要进行中断的单词判断。
code(C++)
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
using namespace std;
class Solution
{
public:
/// <summary>
/// 哈希表
/// </summary>
/// <param name="paragraph"></param>
/// <param name="banned"></param>
/// <returns></returns>
string mostCommonWord(string paragraph, vector<string>& banned)
{
// 先用哈希表存储列表中的字符串
unordered_map<string, int> ban;
for (string b : banned)
{
transLower(b);
ban[b]++;
}
// 然后再用另外一个哈希表存储段落中的非ban单词出现的次数
unordered_map<string, int> time;
int len = paragraph.length();
string ans = "";
string word = "";
// 重新写了循环判断,原来的靠标点符号,有点过于麻烦
// 只需判断是否是字母
for (int i = 0; i < len; ++i)
{
char ch = paragraph[i];
if (ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122)
{
// 大写转小写
if (ch >= 65 && ch <= 90)
{
ch += 32;
}
word += ch;
}
// 去除空字符串的情况带来的影响,保证一定有单词
else if(word != "")
{
// 哈希表判断是否被ban
if (ban[word] != 1)
{
// 该单词的次数+1
time[word]++;
// 动态更新结果答案
if (word != "" && (ans == "" || time[word] > time[ans]))
{
ans = word;
}
}
// 将单词字符串清空
word = "";
}
}
// 最后一个单词的情况
if (ban[word] != 1)
{
time[word]++;
}
if (ans == "" || time[word] > time[ans])
{
ans = word;
}
return ans;
}
private:
// 写一个单词转小写的函数
void transLower(string& word)
{
for (char& ch : word)
{
if (ch >= 65 && ch <= 90)
{
ch += 32;
}
}
}
};
int main(int argc, char** argv)
{
int n;
string par;
while (getline(cin, par))
{
cin >> n;
vector<string> ban(n);
for (string& b : ban)
{
cin >> b;
}
Solution sol;
cout << sol.mostCommonWord(par, ban) << endl;
}
return 0;
}