打算认真搞算法,以后的周赛尽量每次都参加,分数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
}