819. 最常见的单词

180 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

一、题目描述

  • 给定一个段落 (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 只包含字母、空格和下列标点符号!?',;.
    • 不存在没有连字符或者带有连字符的单词。
    • 单词里只包含字母,不会出现省略号或者其他标点符号。

二、思路分析:

  • 要求返回给定字符串中出现次数最多的单词,并且该单词不能出现在禁用列表中
    • 首先是不是需要将字符串段落中的单词提取出来,然后才能统计每个单词出现的次数和是不是在禁用列表中
    • 怎么做?单词和单词之间的区分有两种方式,一种是空格,一种是英文符号 !?',;.
    • 去除掉所有的符号,然后利用字符串的spilt方法将每个单词提取出来
  • 将段落字符串中的每个单词提出出来之后怎么办?
    • 题目描述说单词是不区分大小写的,也就是Allall表示同一个单词,all出现了两次
    • 所以还需要将大写的单词或者部分大写字母转化成小写字母
  • 然后呢?
    • 得到一个字符串的单词数组,怎么统计每个单词的出现次数?统计后怎么得到最高的出现次数的那个单词?并且需要验证这个最高的次数多单词是不是禁用单词,如果是,那就取出现次数第二高的,如果第二高的也是禁用单词,取第三高的。。。。
    • 这里做的是先遍历字符串数组,用toLocaleLowerCase方法将大写字母转化为小写字母
    • 然后利用Set去重后得到一个去重后的新数组
    • 遍历去重前的数组和禁用数组,如果去重前的数组项在禁用数组内出现了,那么这个单词一定不是常用单词,统统删掉
    • 遍历删除禁用单词后的未去重单词列表和去重单词列表,统计每个单词出现的次数和单词,用对象形式储存在数组中
    • 将保存答案的数组排序,排序字段为对象中的index属性,保存的就是该单词出现的次数
    • 最后将排序后的数组内对象的value返回即可

三、AC 代码:

function mostCommonWord(paragraph: string, banned: string[]): string {
    let str = paragraph.replace(/[!?',;.]/g, ' ');
    let strArr = str.split(' ').map(item => item.toLocaleLowerCase());
    let removeArr = [... new Set(strArr)];
    let maxCount = [];
    for(let i = 0; i < banned.length; i++){
        for(let j = 0; j < removeArr.length; j++){
            if(banned[i] === removeArr[j] || removeArr[j] === ''){
                removeArr.splice(j, 1);
                j--
            }
        }
    }
    for(let i = 0; i < removeArr.length; i++){
        let count = 0;
        for(let j = 0; j < strArr.length; j++){
            if(removeArr[i] === strArr[j]){
                count++
            }
        }
        maxCount.push({
            'index': count,
            'value': removeArr[i]
        })
    }
    maxCount.sort((a, b)=> b.index - a.index)
    return maxCount[0].value
};

四、总结:

  • 需要注意的地方:
    • 刚开始将所有符号都替换成空字符,然后提交的时候报错了,错误用例是"a, a, a, a, b,b,b,c, c"和["a"]
    • 原因是有的单词或者字母之间只有符号,替换成空字符后两个单词就会变成一个单词
    • 后面将所有符号替换成空格,然后再按照空格进行切割
      • 这样也会出现切割出来数组中有空字符的情况,所以再遍历删除禁用列表时一起把空字符也删掉
  • 简单难度,就是方法有点麻烦,更多解题方式,移步题解区

image.png