js解leetcode(49)-中等

1.灯泡开关

题目:

现有一个房间,墙上挂有 n 只已经打开的灯泡和 4 个按钮。在进行了 m 次未知操作后,你需要返回这 n 只灯泡可能有多少种不同的状态。

假设这 n 只灯泡被编号为 [1, 2, 3 ..., n],这 4 个按钮的功能如下:

将所有灯泡的状态反转(即开变为关,关变为开)
将编号为偶数的灯泡的状态反转
将编号为奇数的灯泡的状态反转
将编号为 3k+1 的灯泡的状态反转(k = 0, 1, 2, ...)

思路:观察可知,对于第i个灯泡,它与第i+6个灯泡状态相同,所以最终的结果在前6个灯泡中取。再观察,前3个可以推出后3,个,所以枚举即可

时间复杂度度O(1),空间复杂度O(1)

/**
 * @param {number} n
 * @param {number} m
 * @return {number}
 */
var flipLights = function(n, m) {
  n = Math.min(n, 3);
  if (m == 0) return 1;
  if (m == 1) return n == 1 ? 2 : n == 2 ? 3 : 4;
  if (m == 2) return n == 1 ? 2 : n == 2 ? 4 : 7;
  return n == 1 ? 2 : n == 2 ? 4 : 8;
};

2.最长递增子序列的个数

题目:给定一个未排序的整数数组,找到最长递增子序列的个数。

思路:这题和之前一题:最长递增子序列的长度很像,不过这里要统计个数。

还是动态规划,dp[i]记录以该元素结尾的最长子序列的长度以及对应出现的次数,所以用一个数组记录

然后就和之前求长度一样了。双重循环遍历,遇到长度相同的,次数相加。长度更大的,次数重置,长度更新。.最后遍历数组,先找长度的最大值和对应的次数

时间复杂度O(n2),空间复杂度O(n)

/**
 * @param {number[]} nums
 * @return {number}
 */
var findNumberOfLIS = function(nums) {
  const l = nums.length;
  const dp = new Array(l).fill("").map(() => [1, 1]);
  for (let i = 1; i < l; i++) {
    for (let j = i - 1; j >= 0; j--) {
      if (nums[i] > nums[j]) {
        if (dp[j][0] + 1 === dp[i][0]) {
          dp[i][1] += dp[j][1];
        } else if (dp[j][0] + 1 > dp[i][0]) {
          dp[i][0] = dp[j][0] + 1;
          dp[i][1] = dp[j][1];
        }
      }
    }
  }
  let c = 0;
  let max = 0;
  for (const [length, n] of dp) {
    if (length > max) {
      c = n;
      max = length;
    } else if (length === max) {
      c += n;
    }
  }
  return c;
};

3.实现一个魔法字典

题目:

设计一个使用单词列表进行初始化的数据结构,单词列表中的单词 互不相同 。 如果给出一个单词,请判定能否只将这个单词中一个字母换成另一个字母,使得所形成的新单词存在于你构建的字典中。

实现 MagicDictionary 类:

MagicDictionary() 初始化对象
void buildDict(String[] dictionary) 使用字符串数组 dictionary 设定该数据结构,dictionary 中的字符串互不相同
bool search(String searchWord) 给定一个字符串 searchWord ,判定能否只将字符串中 一个 字母换成另一个字母,使得所形成的新字符串能够与字典中的任一字符串匹配。如果可以,返回 true ;否则,返回 false 。

思路:数组存储单词,以及对应的长度。然后查找的时候,先过滤出长度相同的,然后比较每个字符,找到只相差一个的

/**
 * Initialize your data structure here.
 */
var MagicDictionary = function () {
  this.data = [];
  this.length = [];
};

/**
 * @param {string[]} dictionary
 * @return {void}
 */
MagicDictionary.prototype.buildDict = function (dictionary) {
  this.data = dictionary
  this.length = dictionary.map((item) => item.length);
};

/**
 * @param {string} searchWord
 * @return {boolean}
 */
MagicDictionary.prototype.search = function (searchWord) {
  return this.data
    .filter((item, index) => {
      return this.length[index] === searchWord.length;
    })
    .some((item) => {
      let c = 0;
      const l = item.length;
      for (let i = 0; i < l; i++) {
        if (item[i] !== searchWord[i]) c++;
      }
      return c === 1;
    });
};

4.键值映射

题目:

实现一个 MapSum 类,支持两个方法,insert 和 sum:

MapSum() 初始化 MapSum 对象
void insert(String key, int val) 插入 key-val 键值对,字符串表示键 key ,整数表示值 val 。如果键 key 已经存在,那么原来的键值对将被替代成新的键值对。
int sum(string prefix) 返回所有以该前缀 prefix 开头的键 key 的值的总和。

思路:线段树或者数组。

线段树,就是每个单词存储在嵌套的对象里,比如abc:2,这么存储:a:{b:{c:{val:2}}}}

用val的键保存值,最后查找的时候,找到对象,然后一个dfs即可


/**
 * Initialize your data structure here.
 */
var MapSum = function () {
  this.data = {};
};

/**
 * @param {string} key
 * @param {number} val
 * @return {void}
 */
MapSum.prototype.insert = function (key, val) {
  let cur = this.data;
  for (const s of key) {
    if (cur[s]) {
      cur = cur[s];
    } else {
      cur[s] = {};
      cur = cur[s];
    }
  }
  cur.val = val;
};

/**
 * @param {string} prefix
 * @return {number}
 */
MapSum.prototype.sum = function (prefix) {
  let obj = this.data;
  for (const s of prefix) {
    obj = obj[s];
    if (!obj) return 0;
  }
  let v = 0;
  const stack = [obj];
  while (stack.length) {
    const item = stack.shift();
    if(!item)continue
    v += item.val || 0;
    const keys = Object.keys(item).filter((item) => item !== "val");
    stack.push(...keys.map((key) => item[key]));
  }
  return v;
};

也可以用数组,用字符串的startsWith过滤然后求值。不过时间复杂度上更高

/**
 * Initialize your data structure here.
 */
var MapSum = function () {
  this.data = {};
};

/**
 * @param {string} key
 * @param {number} val
 * @return {void}
 */
MapSum.prototype.insert = function (key, val) {
  this.data[key] = val;
};

/**
 * @param {string} prefix
 * @return {number}
 */
MapSum.prototype.sum = function (prefix) {
  let c = 0;
  const keys=Object.keys(this.data)
  for (const key of keys) {
    if (key.startsWith(prefix)) c += this.data[key];
  }
  return c;
};

5.有效的括号字符串

题目:

给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:

任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
* 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。

思路:用一个栈保存左括号的下标,一个栈保存*的下标。因为*可以是左括号也可以是右括号也可以是空值,所以在遇到右括号时,优先将左括号栈的栈顶元素弹出。如果左括号栈没有,就弹出*栈的栈顶元素。如果两个都为空,那么直接返回false

最后,还要判断两个栈内的下标关系。两个栈取最后一个元素,如果左括号栈的下标更大,说明无法匹配。直到左括号栈为空

时间复杂度O(n),空间复杂度O(n)

/**
 * @param {string} s
 * @return {boolean}
 */
var checkValidString = function(s) {
  const stack = [];
  const v = [];
  const l = s.length;
  for (let i = 0; i < l; i++) {
    if (s[i] === "(") {
      stack.push(i);
    } else if (s[i] === "*") {
      v.push(i);
    } else {
      if (!stack.length && !v.length) return false;
      if (stack.length) {
        stack.pop();
      } else {
        v.pop();
      }
    }
  }
  while (stack.length && v.length) {
    if (stack[stack.length - 1] > v[v.length - 1]) return false;
    stack.pop();
    v.pop();
  }
  return !stack.length;
};

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值