『力扣周赛』第 356 次周赛

147 阅读1分钟

打算认真搞算法,以后的周赛尽量每次都参加,分数1529,现阶段目标是稳定做出前三题,冲刺1800分。

本次周赛做出两题:

  • 第一题过于简单,略。
  • 第二题想到暴力做法,但是有地方写复杂了,导致时间复杂度O(n^3),被卡了时间复杂度,于是去思考滑动窗口+双指针,但是没想到怎么在窗口内的值满足条件时,把窗口右边的数加进来,即没想到ans += n - j的操作,花了很多时间也没写出来,最后写出了O(n^2)的暴力。
  • 第三题看了几分钟,没有思路,放弃。
  • 第四题暂且不在考虑范围内。

第二题 - 统计完全子数组的数目

暴力

先把所有数加入集合s,之后遍历nums的每一个[i, j]区间,把这之间的数加入集合_s,判断s和_s的大小是否相等。

var countCompleteSubarrays = function(nums) {
    const s = new Set(nums)
    const len = nums.length
    let ans = 0
    
    for (let i = 0; i < len; i++) {
        const _s = new Set()
        for (let j = i; j < len; j++) {
            _s.add(nums[j])
            if (s.size === _s.size) {
                ans++
            }
        }
        _s.delete(nums[i])
    }
    return ans
};

滑动窗口 + 双指针

滑动窗口加双指针的基本原理不作解释,主要说明一下我是哪里被卡住了。

以[1, 3, 1, 2, 2]为例,当l=0,r=3,即当前滑动窗口为[1, 3, 1, 2]时,此时满足while循环的条件,l应该向右移动,移动后变成[3, 1, 2],那么答案之一的[1, 3, 1, 2, 2]就会被漏掉。周赛时我一直没想到怎么做,正确方法是 ans += len - r:当前窗口已经满足题意,那么右边再多添加几个数依然满足。这样就能做到不重不漏。

var countCompleteSubarrays = function(nums) {
  const s = new Set(nums);
  const len = nums.length;
  let ans = 0, l = 0;
  const m = new Map();

  for (let r = 0; r < len; r++) {
    m.has(nums[r]) 
        ? m.set(nums[r], m.get(nums[r]) + 1) 
        : m.set(nums[r], 1);

    while (m.size === s.size) {
      ans += len - r; // 重要

      m.get(nums[l]) === 1
        ? m.delete(nums[l])
        : m.set(nums[l], m.get(nums[l]) - 1);
      l++;
    }
  }
  return ans;
};

第三题 - 包含三个字符串的最短字符串

周赛没看出来做法,但实际上是一道不太难的模拟,只需要把三个字符串的拼接情况全部列举出来(包括重复段的处理),存入数组strArr中,再遍历每个拼好的字符串,根据“长度最短”和“ascil最小”得到res即可。

js在比较字符串的时候只看第一个不相同字符的ascil值,打比方说,"aaa" < "b",它没有考虑到length。

以"abc"和"bca"为例说明join:重叠长度i = 2,所以res = a + b.substring(2),也就是只用拼接上b的最后一个字符。

var minimumString = function(a, b, c) {
    const strArr = []
    
    // 枚举六种拼接的情况
    strArr.push(join(join(a, b), c))
    strArr.push(join(join(b, a), c))
    strArr.push(join(join(a, c), b))
    strArr.push(join(join(c, a), b))
    strArr.push(join(join(b, c), a))
    strArr.push(join(join(c, b), a))

    let res = strArr[0]

    for (const str of strArr) {
        if (compareString(str, res)) {
            res = str
        }
    }
    return res
};

// 比较两个字符串的大小
function compareString(str1, str2) {
    if (str1.length < str2.length) 
        return true
    // str1 < str2 说明str1第一个不相等的字符的ascil值较小
    else if (str1.length === str2.length && str1 < str2) 
        return true

    return false
}

function join(str1, str2) {
    if (str1.includes(str2)) return str1
    
    // 重叠部分的最大长度
    let w = Math.min(str1.length, str2.length)
    // 重叠部分的真正长度
    let i = w

    for (; i > 0; i--) {
        if (check(str1, str2, i)) {
            break
        }
    }
    return str1 + str2.substring(i)
}

function check(str1, str2, index) {
    let i = str1.length - index, j = 0
    for (; i < str1.length; i++, j++) {
        if (str1[i] !== str2[j]) {
            return false
        }
    }
    return true
}