华为OD机试题库《C++》限时优惠 9.9
华为OD机试题库《Python》限时优惠 9.9
华为OD机试题库《JavaScript》限时优惠 9.9
针对刷题难,效率慢,我们提供一对一算法辅导, 针对个人情况定制化的提高计划(全称1V1效率更高)。
看不懂有疑问需要答疑辅导欢迎私VX: code5bug
题目描述
给你一个字符串 str和整数k,返回满足以下条件的所有子字符串个数:
- 恰好包含k 个字母
- 数字0-9各出现至少一次
输入描述
第一行为字符串 str (1 <= length <= 100000),仅包含数字和小写字母。
第二行为整数k(0 <= k <= 100000)
输出描述
输出一个整数,表示满足条件的所有子字符串个数
补充说明
子字符串 是字符串中连续的 非空 字符序列
示例1
输入:
a0123456789aa
1
输出:
2
示例2
输入:
aa01234a5678901234aa
1
输出:
22
题解
- 滑动窗口法:使用两个滑动窗口来维护满足条件的子字符串。
- 窗口1(left1):指向满足恰好k个字母且所有数字0-9都出现至少一次的最早左边界。
- 窗口2(left2):指向不满足恰好k个字母且所有数字0-9都出现至少一次的最早左边界。
- 双指针维护:使用两个指针
left1
和left2
来分别维护窗口1和窗口2的左边界。- 统计数字频率:使用哈希表(或字典)来统计当前窗口中数字的出现频率,确保所有数字0-9都至少出现一次。
- 计算有效子字符串:对于每个右指针
right
,计算满足条件的子字符串的左指针范围[left1, left2)
,并将该范围内的子字符串数量累加到结果中。
JavaScript
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
});
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
// Author: code5bug
(async () => {
const s = await readline();
const k = parseInt(await readline());
// 两个窗口中的字符个数计数和对应的左指针
let letter_count1 = 0, left1 = 0;
let letter_count2 = 0, left2 = 0;
// 对两个窗口出现的数字进行计数统计
const digit_freq1 = new Map();
const digit_freq2 = new Map();
// 满足要求的子串个数
let ans = 0;
for (let right = 0; right < s.length; right++) {
const c = s[right];
if (/[a-zA-Z]/.test(c)) {
letter_count1++;
letter_count2++;
} else {
digit_freq1.set(c, (digit_freq1.get(c) || 0) + 1);
digit_freq2.set(c, (digit_freq2.get(c) || 0) + 1);
}
// 窗口形成的子串不满足要求则继续扩大窗口
if (letter_count2 < k || digit_freq2.size < 10) {
continue;
}
// 为了形成 [left1, left2) ~ right 的子串都是满足要求的子字符串
// 所以 left2 为最早不满足要求的位置
while (letter_count2 >= k && digit_freq2.size === 10) {
const left_char = s[left2];
left2++;
if (/[a-zA-Z]/.test(left_char)) {
letter_count2--;
} else {
if (digit_freq2.get(left_char) === 1) {
digit_freq2.delete(left_char);
} else {
digit_freq2.set(left_char, digit_freq2.get(left_char) - 1);
}
}
}
// left1 为满足要求的最早的位置
while (letter_count1 > k) {
const left_char = s[left1];
left1++;
if (/[a-zA-Z]/.test(left_char)) {
letter_count1--;
} else {
if (digit_freq1.get(left_char) === 1) {
digit_freq1.delete(left_char);
} else {
digit_freq1.set(left_char, digit_freq1.get(left_char) - 1);
}
}
}
// left1 和 left2 之间的区间就是满足题目要求子串的左指针有效取值范围
ans += left2 - left1;
}
console.log(ans);
rl.close();
})();
希望这个专栏能让您熟练掌握算法, 🎁🎁🎁。
整理题解不易, 如果有帮助到您,请给点个赞 ❤️ 和收藏 ⭐,让更多的人看到。🙏🙏🙏