「1832. 判断句子是否为全字母句」题解

4/24/2021 LeetCodeAlgorithm

# 1832. 判断句子是否为全字母句 (opens new window)

# 题目描述

全字母句 指包含英语字母表中每个字母至少一次的句子。

给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句

如果是,返回 true ;否则,返回 false

示例 1:

输入:sentence = "thequickbrownfoxjumpsoverthelazydog"
输出:true
解释:sentence 包含英语字母表中每个字母至少一次。

示例 2:

输入:sentence = "leetcode"
输出:false

提示:

  • 1 <= sentence.length <= 1000
  • sentence 由小写英语字母组成

相关信息

  • 难度:简单
  • 标签:字符串

# 题解

此题是我在准备开始参加周赛之际在上一次周赛的虚拟竞赛中做的。虽然是虚拟竞赛,但是体验了一把,感觉还不错,能做出两道题目,对于我这算法菜鸡来说,真是莫大的鼓励。

这道题目主要的实现思路主要有两种:

  1. 遍历 26 个字母,判断其全都在 sentence 中。
  2. 遍历 sentence 所有字符,判断其是否包含全部 26 个字母。

由于 sentence 的长度是不定的,其范围在 1 到 1000 以内,所以我跟倾向于选择第一种思路来实现,这样可以减少遍历消耗。

# 方法一:ASCII 码+简单循环

由于题目中有说明是小写字母,故考虑使用 ASCII 码值来循环。此方法虽看起来简单,但是效率还是不错的,时间空间的消耗都超过了百分之九十。

/**
 * @param {string} sentence
 * @return {boolean}
 */
var checkIfPangram = function (sentence) {
  if (sentence.length < 26) return false;
  //长度小于26,必然不是。
  let isHave;
  for (let i = 97; i < 123; i++) {
    isHave = sentence.indexOf(String.fromCharCode(i));
    //String.indexOf() 查找是否含有子串,成功返回子串开头的下标,失败返回-1
    //String.fromCharCode() 将 ASCII 码值转换为对应的码值。
    if (isHave == -1) return false;
  }
  return true;
};

上边是我想到并实现的方法,很明显,它是前文所言的第一种实现思路。

下面还有许多奇妙的方法,都是从各种大佬的题解中学习到的一些方法,在此做个总结。我会贴出原代码以及写出我觉得可优化的提议并贴出优化后的代码:

# 方法二:转化为数组,并使用 filter()去重

//原代码来自:https://leetcode-cn.com/problems/check-if-the-sentence-is-pangram/solution/js-by-mei-mei-xiang-xue-cyu-yan-a2me/
var checkIfPangram = function (sentence) {
  return sentence.split("").filter(function (item, index, arr) {
    return arr.indexOf(item) === index;
  }).length == 26
    ? true
    : false;
};

这段代码首先使用 split() 方法将 sentence 转化为了字符数组,然后使用了 filter() 来过滤重复字符,实现判断重复的最关键的是使用 indexOf() 方法,因为 indexOf() 始终返回的是从头至尾首次出现的元素的下标或者-1,倘若字符的下标和 indexOf() 不符合,证明其不是首次出现。

这段代码,看代码形式是想写成一行代码解决的样子,但是其中有些写法稍微有点儿繁杂,主要有两点:

  1. filter 的回调函数可以写成箭头函数形式,更简洁
  2. 最后的三元表达式没必要写,==返回值与所写的值是一样的。
  3. //优化后的代码
    var checkIfPangram = function (sentence) {
      return (
        sentence.split("").filter((item, index, arr) => arr.indexOf(item) === index)
          .length === 26
      );
    };
    
//优化后的代码
var checkIfPangram = function (sentence) {
  return (
    sentence.split("").filter((item, index, arr) => arr.indexOf(item) === index)
      .length === 26
  );
};

# 方法三:使用 Set 去重

前面方法二最关键的点便是去重,但其使用的是 filter 去重。这里用到了更高级的数据结构——Set,利用 Set 不能存储重复键值对的特性实现去重。

//原代码来自:https://leetcode-cn.com/problems/check-if-the-sentence-is-pangram/solution/js-qu-zhong-pai-xu-by-jingguangyan-yrjm/
var checkIfPangram = function (sentence) {
  if (sentence.length < 26) return false;
  let arr = Array.from(new Set(sentence));
  arr.sort();

  return arr.join("") === "abcdefghijklmnopqrstuvwxyz";
};

这段代码是先使用 Set 去重,然后使用 Array.from() 转化为数组,然后使用 Array.form() 排序,排序后再使用 join("") 连接成字符串并与'abcdefghijklmnopqrstuvwxyz'比较。

这段代码的亮点是直接将 sentence 传入了 Set() 中做去重,但是在后续判断是否包含全部字母时的处理有些复杂,多了转数组,排序,然后转字符串并比较的过程。其实此时想要判断是否包含全部字母,只需要看 Set.size 属性值是否为 26 便好。因为题目的提示中有说 sentence 全部由小写字母构成,不会有其他字符哒。

//原代码来自:https://leetcode-cn.com/problems/check-if-the-sentence-is-pangram/solution/pan-duan-ju-zi-shi-fou-wei-quan-zi-mu-ju-pjrf/
var checkIfPangram = function (sentence) {
  var newSet = new Set();

  for (var i = 0; i < sentence.length; i++) {
    newSet.add(sentence[i]);
  }

  if (newSet.size == 26) {
    return true;
  } else {
    return false;
  }
};

这段代码是先新建了一个 Set 对象 newSet,然后使用了一个 for 循环将每个字符添加到 newSet 中。最后判断长度是否等于 26。

这段代码很简洁,很容易让人理解,不过还有两点可以优化的:

  1. 不必使用 for 循环,new Set() 可以直接传入一段字符串哒。当传入一段字符串时,它可以自动根据字符去重,且区分大小写,更详细的用法请参考MDN (opens new window)
  2. 最后这个 if 语句写得有些复杂了,完全可以用一个 return 语句替代
  3. var checkIfPangram = function (sentence) {
      var newSet = new Set(sentence);
      return newSet.size === 26;
    };
    
var checkIfPangram = function (sentence) {
  var newSet = new Set(sentence);
  return newSet.size === 26;
};

再简单一步的话,可以直接写成一行代码解决形式:

var checkIfPangram = function (sentence) {
  return new Set(sentence).size === 26;
};

# 方法四:利用 Map 去重

前面已经利用 filter 和 set 去重了,但其实 map 也能去重。What?不应该是 Set 吗?

//原代码来自:https://leetcode-cn.com/problems/check-if-the-sentence-is-pangram/solution/li-yong-mapqu-zhong-bing-jian-zhi-by-qyu-3dy1/
var checkIfPangram = function (sentence) {
  let map = new Map(),
    len = sentence.length;
  for (let i = 0; i < len; i++) {
    map.set(sentence[i]);
    //这里利用了map对象的key值不可重复的特性来达到去重的目的
    if (map.size === 26) return true;
  }
  return false;
};

很惊奇吧?觉得自己学了一个假的 JS?我也是第一次看到这样的用法,日常中去重使用 Set 还是较多的,第一次看见有人使用 map 的 key 值不可重复的特性来达到去重的目的。这很极客~

但是我还是不太推荐这样的用法,毕竟这样的用法可读性较差,如果不是对 Map 所有属性极其了解,很难读懂这段代码,而且它的用法确实也不够规范。

我个人理解的更规范的用法应该如下:

var checkIfPangram = function (sentence) {
  let newMap = new Map();
  for (let item of sentence) {
    if (!newMap.get(item)) {
      newMap.set(item, 1);
      //这个if的位置是个小细节
      if (newMap.size === 26) return true;
    }
  }
  return false;
};

上边代码中返回 true 的 if 判断是一个小细节,其实这个 if 不是一定要放在另一个 if 里面的,只是放在里面可以减少执行它的次数。因为 newMap.size 的值的改变都是在执行的 set 操作之后,这时候判断 size 值是最合适的,如果将其放在另一个 if 外,则每次循环都会进行判断,及时 size 值没有改变也会重复判断,这样就多余了。

以上就是本题的所有题解啦,感谢你能看到这里,如果本文对你有所帮助的话,别忘了给一个 Star 嗷~ 如果你对题解中的代码有不一样的优化意见,也欢迎你在 issue 中指出~ 最重要的是不要忘了点点关注嗷(Github (opens new window)力扣 (opens new window)),以便获取我最新的题解以及文章通知。

# 参考:

# 全部代码

/*
 * @lc app=leetcode.cn id=1832 lang=javascript
 *
 * [1832] 判断句子是否为全字母句
 *
 * https://leetcode-cn.com/problems/check-if-the-sentence-is-pangram/description/
 *
 * algorithms
 * Easy (84.47%)
 * Likes:    2
 * Dislikes: 0
 * Total Accepted:    6.8K
 * Total Submissions: 8.1K
 * Testcase Example:  '"thequickbrownfoxjumpsoverthelazydog"'
 *
 * 全字母句 指包含英语字母表中每个字母至少一次的句子。
 * 
 * 给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句 。
 * 
 * 如果是,返回 true ;否则,返回 false 。
 * 
 * 
 * 
 * 示例 1:
 * 
 * 
 * 输入:sentence = "thequickbrownfoxjumpsoverthelazydog"
 * 输出:true
 * 解释:sentence 包含英语字母表中每个字母至少一次。
 * 
 * 
 * 示例 2:
 * 
 * 
 * 输入:sentence = "leetcode"
 * 输出:false
 * 
 * 
 * 
 * 
 * 提示:
 * 
 * 
 * 1 
 * sentence 由小写英语字母组成
 * 
 * 
 */

// @lc code=start
/**
 * @param {string} sentence
 * @return {boolean}
 */
var checkIfPangram = function (sentence) {
    let newMap = new Map();
    for (let item of sentence) {
        if (!newMap.get(item)) {
            newMap.set(item, 1)
            if (newMap.size === 26) return true;
        }
    }
    return false;
};
// @lc code=end

var checkIfPangram = function (sentence) {
    if (sentence.length < 26) return false;
    let isHave;
    for (let i = 97; i < 123; i++) {
        isHave = sentence.indexOf(String.fromCharCode(i));
        if (isHave == -1) return false;
    }
    return true;
};

var checkIfPangram = function (sentence) {
    //filter去重
    return sentence.split('').filter((item, index, arr) => arr.indexOf(item) === index).length === 26

};

var checkIfPangram = function (sentence) {
    var newSet = new Set(sentence);
    return newSet.size == 26;
};

var checkIfPangram = function (sentence) {
    //直接 set 去重
    return new Set(sentence).size === 26;
};