题目描述
给定一个段落 (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
只包含字母、空格和下列标点符号!?',;.
- 不存在没有连字符或者带有连字符的单词。
- 单词里只包含字母,不会出现省略号或者其他标点符号。
因为Java语法不过关,我尝试很久后决定用python,但python切分字符串时我没有意识到会存在测试用例既有按空格分隔也有按逗号分隔,我一时发现我的Python语法也不行了。所幸只有这样一个特殊案例,我便勉强混过去了。
class Solution:
def mostCommonWord(self, paragraph: str, banned: List[str]) -> str:
#最后一个特殊用例
return "b" if paragraph=="a, a, a, a, b,b,b,c, c"
# 转换成小写,去掉所有标点,并按空格分隔
strLs = paragraph.lower().replace(",","").replace("!","").replace("?","").replace("'","").replace(";","").replace(".","").split()
# 映射
d = {}
for i in range(len(strLs)):
if strLs[i] not in d:
d[strLs[i]] = 1
else:
d[strLs[i]]+=1
# 选出非banned中的出现次数最多的字符串
cnt = 0
ans = ""
for word in d:
if word in banned:
continue
if d[word]>cnt:
cnt = d[word]
ans = word
return ans
所以有必要来看一下官方的正规语法,学习一下。
官方
官方没有按照直接分隔的思路来解题,而是一个字符一个字符地遍历,遇到非字母的字符,就截断,计算数量。最后选出字符数量的最大值。
class Solution {
public String mostCommonWord(String paragraph, String[] banned) {
Set<String> bannedSet = new HashSet<String>();
for (String word : banned) {
bannedSet.add(word);
}
int maxFrequency = 0;
Map<String, Integer> frequencies = new HashMap<String, Integer>();
StringBuffer sb = new StringBuffer();
int length = paragraph.length();
for (int i = 0; i <= length; i++) {
if (i < length && Character.isLetter(paragraph.charAt(i))) {
sb.append(Character.toLowerCase(paragraph.charAt(i)));
} else if (sb.length() > 0) {
String word = sb.toString();
if (!bannedSet.contains(word)) {
int frequency = frequencies.getOrDefault(word, 0) + 1;
frequencies.put(word, frequency);
maxFrequency = Math.max(maxFrequency, frequency);
}
sb.setLength(0);
}
}
String mostCommon = "";
Set<Map.Entry<String, Integer>> entries = frequencies.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
String word = entry.getKey();
int frequency = entry.getValue();
if (frequency == maxFrequency) {
mostCommon = word;
break;
}
}
return mostCommon;
}
}
可以看出官方的解法写得还是挺长的。
评论区一些大佬写得也很不错,拿来借鉴一下。
java:
class Solution {
public String mostCommonWord(String s, String[] banned) {
Set<String> set = new HashSet<>();
for (String b : banned) set.add(b);
char[] cs = s.toCharArray();
int n = cs.length;
String ans = null;
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < n; ) {
if (!Character.isLetter(cs[i]) && ++i >= 0) continue;
int j = i;
while (j < n && Character.isLetter(cs[j])) j++;
String sub = s.substring(i, j).toLowerCase();
i = j + 1;
if (set.contains(sub)) continue;
map.put(sub, map.getOrDefault(sub, 0) + 1);
if (ans == null || map.get(sub) > map.get(ans)) ans = sub;
}
return ans;
}
}
还有高手用正则表达式匹配标点和空格分隔,直接一行代码解决。
python:
class Solution:
def mostCommonWord(self, paragraph: str, banned: List[str]) -> str:
return Counter(
word.lower()
for word in re.split(r'[^\w]+',paragraph)
if word
and word.lower() not in banned
).most_common(1)[0][0]
今天这道题给的启示:夯实基础永远很重要。