代码随想录二刷-哈希表-有效的字母异位词及其相关题目(JS)

242.有效的字母异位词

题目

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:

输入: s = “rat”, t = “car”
输出: false

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • st 仅包含小写字母

方法

因为本题和字母相关,所以使用数组作为哈希表

遍历一遍s字符串,存入不同字符的个数

再遍历一遍t字符串,遇到的字符数组中都–

如果按照题意,则数组中所有字符都应该是0

遍历一遍如果不为0,则return false;遍历结束都符合条件,则return true;

代码

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isAnagram = function(s, t) {
    let res = new Array(26).fill(0);
    // JS与Java语言的差异,Java中可以直接-'a'
    // JS中会返回错误结果,需要使用charCodeAt()方法返回该字符的unicode值
    for (let i = 0;i < s.length;i++){
        res[s[i].charCodeAt() -'a'.charCodeAt()]++;
    }
    for (let i = 0;i < t.length;i++){
        res[t[i].charCodeAt() - 'a'.charCodeAt()]--;
    }
    for (let i = 0;i < res.length;i++){
        if (res[i] !== 0){
            return false;
        }
    }
    return true;
};

383.赎金信

题目

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = “a”, magazine = “b”
输出:false
示例 2:

输入:ransomNote = “aa”, magazine = “ab”
输出:false
示例 3:

输入:ransomNote = “aa”, magazine = “aab”
输出:true

提示:

1 <= ransomNote.length, magazine.length <= 10^5
ransomNote 和 magazine 由小写英文字母组成

方法

本题只和字母相关,所以选择数组作为哈希表

由题意可知,magazine中的各字母数量要大于等于ransomNote各字母数量

其他方法同有效的字母异位词,在最后的for循环中只需满足res数组中的各元素大于等于0即可

代码

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
    let res = new Array(26).fill(0);
    let base = "a".charCodeAt();
    for (let i of magazine) {
        res[i.charCodeAt() - base]++;
    }
    for (let i of ransomNote) {
        res[i.charCodeAt() - base]--;
    }
    for (let i of res){
        if (i < 0) {
            return false;
        }
    }
    return true;
};

49.字母异位词分组

题目

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:

输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]
示例 2:

输入: strs = [“”]
输出: [[“”]]
示例 3:

输入: strs = [“a”]
输出: [[“a”]]

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

方法

自己的方法

// strs.lenth = 0 和 1时,单独处理

// 我的思路是将有效的字母异位词封装成一个函数

// 两层循环找相等(需要加入Set集合中去重)

// Set 再转化为数组,加入res数组中

// 时间和空间都很大

【很传统的方法,太绕了,还容易出现一些不知道如何排查的错误】

方法一:排序

由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。

作者:LeetCode-Solution

方法二:计数

(代码有点迷糊,不推荐该方法)

由于互为字母异位词的两个字符串包含的字母相同,因此两个字符串中的相同字母出现的次数一定是相同的,故可以将每个字母出现的次数使用字符串表示,作为哈希表的键。

由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为 26 的数组记录每个字母出现的次数。需要注意的是,在使用数组作为哈希表的键时,不同语言的支持程度不同,因此不同语言的实现方式也不同。

代码

// 方法一 排序
var groupAnagrams = function(strs) {
    const map = new Map();
    for (let str of strs) {
        // 将字符串转化为数组,方便使用数组的排序方法
        let array = Array.from(str);
        array.sort();
        let key = array.toString();
        // map中是否含有key,含有则还是赋值给list,没有就创建
        // 这一步简单 但很重要
        let list = map.get(key) ? map.get(key) : new Array();
        // 因为之前的排序,符合条件的不同元素转化为相同的key
        // 基于相同的key,我们把对应原来的str加入进去
        list.push(str);
        // 更新key对应的value
        map.set(key, list);
    }
    // 取出各map中的值
    return Array.from(map.values());
};

几个神奇的函数:

1.Array.from(element);看起来好像万物皆可转化为数组

Array.from(str) 字符串转化为数组

Array.from(set) 集合转化为数组

Array.from(map.values()):map的值集合转化为数组

详细可参考

https://blog.csdn.net/m0_53375764/article/details/124056984

2.数组转化为字符串,array.toString()

3.字母可排序:array.sort()

var groupAnagrams = function(strs) {
    const map = new Object();
    for (let s of strs) {
        const count = new Array(26).fill(0);
        for (let c of s) {
            count[c.charCodeAt() - 'a'.charCodeAt()]++;
        }
        map[count] ? map[count].push(s) : map[count] = [s];
    }
    return Object.values(map);
};

438.找到字符串中所有字母异位词

题目

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。
示例 2:

输入: s = “abab”, p = “ab”
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • sp 仅包含小写字母

方法

自己的方法

结合有效的字母异位词问题,使用for循环遍历一遍,暴力解法

滑动窗口:

【要掌握】

其实与数组里面的滑动窗口类似,只不过这是字符类型

根据题目要求,我们需要在字符串 ss 寻找字符串 pp 的异位词。因为字符串 pp 的异位词的长度一定与字符串 pp 的长度相同,所以我们可以在字符串 ss 中构造一个长度为与字符串 pp 的长度相同的滑动窗口,并在滑动中维护窗口中每种字母的数量;当窗口中每种字母的数量与字符串 pp 中每种字母的数量相同时,则说明当前窗口为字符串 pp 的异位词。

在算法的实现中,我们可以使用数组来存储字符串 pp 和滑动窗口中每种字母的数量。

细节

当字符串 ss 的长度小于字符串 pp 的长度时,字符串 ss 中一定不存在字符串 pp 的异位词。但是因为字符串 ss 中无法构造长度与字符串 pp 的长度相同的窗口,所以这种情况需要单独处理。

作者:LeetCode-Solution

代码

/**
 * @param {string} s
 * @param {string} p
 * @return {number[]}
 */
var findAnagrams = function(s, p) {
    let res = new Array();
    for (let i = 0;i < s.length - p.length + 1;i++){
        let temp = s.substring(i,i + p.length);
        if (isAnagram(temp,p)){
            res[res.length] = i;
        }
    }
    return res;
};
var isAnagram = function(s, t) {
    let res = new Array(26).fill(0);
    // JS与Java语言的差异,Java中可以直接-'a'
    // JS中会返回错误结果,需要使用charCodeAt()方法返回该字符的unicode值
    for (let i = 0;i < s.length;i++){
        res[s[i].charCodeAt() -'a'.charCodeAt()]++;
    }
    for (let i = 0;i < t.length;i++){
        res[t[i].charCodeAt() - 'a'.charCodeAt()]--;
    }
    for (let i = 0;i < res.length;i++){
        if (res[i] !== 0){
            return false;
        }
    }
    return true;
};

滑动窗口

var findAnagrams = function(s, p) {
    const sLen = s.length, pLen = p.length;

    if (sLen < pLen) {
        return [];
    }

    const ans = [];
    const sCount = new Array(26).fill(0);
    const pCount = new Array(26).fill(0);
    for (let i = 0; i < pLen; ++i) {
        ++sCount[s[i].charCodeAt() - 'a'.charCodeAt()];
        ++pCount[p[i].charCodeAt() - 'a'.charCodeAt()];
    }

    if (sCount.toString() === pCount.toString()) {
        ans.push(0);
    }

    for (let i = 0; i < sLen - pLen; ++i) {
        --sCount[s[i].charCodeAt() - 'a'.charCodeAt()];
        ++sCount[s[i + pLen].charCodeAt() - 'a'.charCodeAt()];

        if (sCount.toString() === pCount.toString()) {
            ans.push(i + 1);
        }
    }

    return ans;
};

本道题目与最小覆盖子串比起来算是简单的
主要体现在本题的字符串都是小写字母,而最小覆盖子串字符大小写均可
最重要的是,本题求字母异位词,保证字符数量一样即可,所以可以用sCount.toString() == pCount.toString()来保证是否符合条件;而最小覆盖子串s覆盖t,说明移动到的s位置包含t的各字符,且s中该字符的数量要大于t 中该字符的数量,只能用map比较。

你可能感兴趣的:(javascript)