Leetcode n.438题 JavaScript

67 阅读3分钟

记录一下刷题学到的新知识,不得不说实际写和阅读学习的收获是不一样的

题目:

给定两个字符串 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
  • s 和 p 仅包含小写字母

题目刷多了之后我发现很多题都是在后面会有一些比较长的检查点,这时候对于性能优化就比较重要了,有些虽然可以在ide跑出来,但是可能力扣设置了超时时间会显示超过运行时间。

优化一般可以从算法和数据结构入手,比如前一篇文章就是把数据结构从数组改到哈希表(对象)从而提高性能;算法方面就是今天要提及的改进

思路: 1.一开始我的思路是先遍历大串,从每个位置截取和小串长度相同的子串,然后比较子串和小串的组成字母是否相同,相同则把序号i存入res

2.比较小串字母组成我是直接转成数组排序+join合成字符串。这种方法的特点就是简单容易想。代码如下:

var findAnagrams = function(s, p) {
  let res = [];
  s=[...s]
  p=[...p]
  let i = 0;let j=0
  let len=p.length
  let temp=""
  while(i<s.length){
    temp=s.slice(i,i+len).sort()
    if(temp.join("")==p.sort().join("")){
      res.push(i)
    }
    i++
  }
return res
  
};

console.log(findAnagrams("cbaebabacd", "abc"));

但是这种方法的缺点就是比较计算量大,每次滑动窗口更新的时候都要进行一次排序。因此在后面的长字符串检测的时候挂了。

3.由于时间复杂度不算太高,数据结构我也想不出来有什么方法改进,所以就从检测字符串的算法来改进。要去比较每次取得的字符串和小串的字母组成是否相同,而且要更改尽量少(排序的开销比较大)。对于每次滑动窗口来说,对取得的子串只改动一个字母,所以是否可以存储每次的子串,并且可以单独修改某个字母。对于字母来说最多26个,可以维护一个map或者说对象的结构,键为字母,值为对象,维护结构时只要增减对应的字母的数量就可以。代码如下:

var findAnagrams = function(s, p) {
  let res = [];
  let sArr = Array.from(s);
  let pArr = Array.from(p);
  let len = p.length;
  let pCounts = {};
  let windowCounts = {};
  for (let char of pArr) {
    pCounts[char] = (pCounts[char] || 0) + 1;
  }
  for (let i = 0; i < len; i++) {
    windowCounts[sArr[i]] = (windowCounts[sArr[i]] || 0) + 1;
  }
  for (let i = len; i < s.length; i++) {
    // Check if the window has the same character counts as the pattern
    if (compareObjects(windowCounts, pCounts)) {
      res.push(i - len);
    }

    windowCounts[sArr[i]] = (windowCounts[sArr[i]] || 0) + 1;
    windowCounts[sArr[i - len]]--;
    if (windowCounts[sArr[i - len]] === 0) {
      delete windowCounts[sArr[i - len]];
    }
  }

  if (compareObjects(windowCounts, pCounts)) {
    res.push(s.length - len);
  }

  return res;
};
//比较两个对象
function compareObjects(obj1, obj2) {
  for (let key in obj1) {
    if (obj1[key] !== obj2[key]) {
      return false;
    }
  }
  for (let key in obj2) {
    if (obj2[key] !== obj1[key]) {
      return false;
    }
  }
  return true;
}

console.log(findAnagrams("cbaebabacd", "abc"));

比较数组需要单独进行循环,因为数组是引用对象,地址是不会相同的

image.png