LeetCode - 819 - 最常见的单词(most-common-word)

恭喜自己 GitHub 仓库已破 200 道 LeetCode 简单题题解~

虽然没有其他公众号的文章一样受欢迎(枯燥),虽然不曾连续发掘金、SF 等平台(太多发了没有观赏性),但是从中真的得到了许许多多的快乐,忘掉了工作的枯燥和生活的苦闷,2020,加油!

GitHub 仓库地址:https://github.com/LiangJunrong/document-library/blob/master/other-library/LeetCode/easy/README.md

一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 前言
三 解题及测试
四 LeetCode Submit
五 解题思路
六 进一步思考

二 前言

  • 难度:简单

  • 涉及知识:字符串

  • 题目地址:https://leetcode-cn.com/problems/most-common-word/

  • 题目内容

给定一个段落 (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.
1 <= 禁用单词个数 <= 100.
1 <= 禁用单词长度 <= 10.

答案是唯一的, 且都是小写字母 
(即使在 paragraph 里是大写的,
即使是一些特定的名词,答案都是小写的。)
paragraph 只包含字母、空格和下列标点符号!?',;.
不存在没有连字符或者带有连字符的单词。
单词里只包含字母,不会出现省略号或者其他标点符号。

三 解题及测试

小伙伴可以先自己在本地尝试解题,再回来看看 jsliang 的解题思路。

  • LeetCode 给定函数体

/**
 * @param {string} paragraph
 * @param {string[]} banned
 * @return {string}
 */
var mostCommonWord = function(paragraph, banned) {
    
};

根据上面的已知函数,尝试破解本题吧~

确定了自己的答案再看下面代码哈~

index.js

/**
 * @name 最常见的单词
 * @param {string} paragraph
 * @param {string[]} banned
 * @return {string}
 */
const mostCommonWord = (paragraph, banned)  =>{
  // 通过正则转换字符串为数组
  const strToArr = paragraph.match(/[a-zA-Z]+/g);
  if (strToArr.length === 1) {
    return strToArr[0].toLowerCase();
  }

  // 定义哈希表
  const map = new Map();

  // 定义最大次数和对应的字符串
  let max = Number.MIN_SAFE_INTEGER;
  let result = '';

  // 遍历数组
  for (let i = 0; i < strToArr.length; i++) {
    // 转换小写字母
    strToArr[i] = strToArr[i].toLowerCase();

    // 统计次数和是否禁用
    const time = map.get(strToArr[i]) || 0;
    const index = banned.findIndex(item => item === strToArr[i]);

    // 累积出现次数
    if (time) {
      map.set(strToArr[i], time + 1);
    } else {
      map.set(strToArr[i], 1);
    }

    // 判断是否需要替换
    if (time + 1 > max && index === -1) {
      max = time + 1;
      result = strToArr[i];
    }
  }

  // 返回结果
  return result;
};

console.log(mostCommonWord('Bob hit a ball, the hit BALL flew far after it was hit.', ['hit'])); // ball
console.log(mostCommonWord('a.', []));
console.log(mostCommonWord('Bob. hIt, baLl', ['bob', 'hit']));

node index.js 返回:

ball
a
ball

四 LeetCode Submit

Accepted
* 47/47 cases passed (68 ms)
* Your runtime beats 86.51 % of javascript submissions
* Your memory usage beats 33.33 % of javascript submissions (36.8 MB)

五 解题思路

首先,先上代码:

const mostCommonWord = (paragraph, banned)  =>{
 // 1. 通过正则转换字符串为数组
 const strToArr = paragraph.match(/[a-zA-Z]+/g);

 // 2. 定义哈希表
 const map = new Map();

 // 3. 定义最大次数和对应的字符串
 let max = Number.MIN_SAFE_INTEGER;
 let result = '';

 // 4. 遍历数组
 for (let i = 0; i < strToArr.length; i++) {
   // 5. 转换当前值为小写字母
   strToArr[i] = strToArr[i].toLowerCase();

   // 6. 统计当前值次数和是否禁用
   const time = map.get(strToArr[i]) || 0;
   const index = banned.findIndex(item => item === strToArr[i]);

   // 7. 累积当前值出现次数
   if (time) {
     map.set(strToArr[i], time + 1);
   } else {
     map.set(strToArr[i], 1);
   }

   // 8. 判断最大次数和对应字符串是否需要替换
   if (time + 1 > max && index === -1) {
     max = time + 1;
     result = strToArr[i];
   }
 }

 // 9. 返回结果
 return result;
};

内容甚是简单,为了便于阅读,就加上了 9 条注释,小伙伴可以直接阅读。

Submit 提交:

Accepted
* 47/47 cases passed (68 ms)
* Your runtime beats 86.51 % of javascript submissions
* Your memory usage beats 33.33 % of javascript submissions (36.8 MB)

当然,这过程是复杂了:

  1. 用了正则 match

  2. 用了双重循环 for + findIndex

六 进一步思考

当然,针对自身不足,希望能找到不错的方法~

简化暴力

const mostCommonWord = (paragraph, banned) => {
  const wordList = paragraph.toLowerCase().match(/\w+/g);
  const result = {};
  for (let i = 0; i < wordList.length; i++) {
    if (!banned.includes(wordList[i]) && result[wordList[i]]) {
      result[wordList[i]] += 1;
    } else if (!banned.includes(wordList[i]) && !result[wordList[i]]) {
      result[wordList[i]] = 1;
    }
  }
  let count = 0;
  let maxItem = '';
  for (const key in result) {
    if (result[key] > count) {
      count = result[key];
      maxItem = key;
    }
  }
  return maxItem;
}

看起来清爽一点,不过效率同样不高:

Accepted
* 47/47 cases passed (84 ms)
* Your runtime beats 15.08 % of javascript submissions
* Your memory usage beats 80 % of javascript submissions (35.5 MB)

还有空间节省到了极致的:

空间节省

var mostCommonWord = function (paragraph, banned) {
  let arr = paragraph.toLowerCase().match(/\w+/g);
  let map = new  Map();
  for (let i = 0; i < arr.length; i++) {
    if (!banned.includes(arr[i])) {
      let count = map.get(arr[i]) === undefined ? 0 : map.get(arr[i]);
      map.set(arr[i], ++count);
    }
  }
  let key = Array.from(map);
  key.sort(function (a, b) {
    return b[1] - a[1];
  })
  return key[0][0];
};

Submit 提交:

Accepted
* 47/47 cases passed (80 ms)
* Your runtime beats 26.19 % of javascript submissions
* Your memory usage beats 100 % of javascript submissions (34.8 MB)

如果小伙伴有效率更好的方法,欢迎评论留言或者私聊 jsliang~


不折腾的前端,和咸鱼有什么区别!

jsliang 会每天更新一道 LeetCode 题解,从而帮助小伙伴们夯实原生 JS 基础,了解与学习算法与数据结构。

浪子神剑 会每天更新面试题,以面试题为驱动来带动大家学习,坚持每天学习与思考,每天进步一点!

扫描上方二维码,关注 jsliang 的公众号(左)和 浪子神剑 的公众号(右),让我们一起折腾!

jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于https://github.com/LiangJunrong/document-library上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值