字符串计数匹配 - 华为OD机试真题(JavaScript题解)

华为OD机试题库《C++》限时优惠 9.9

华为OD机试题库《Python》限时优惠 9.9

华为OD机试题库《JavaScript》限时优惠 9.9

针对刷题难,效率慢,我们提供一对一算法辅导, 针对个人情况定制化的提高计划(全称1V1效率更高)。

看不懂有疑问需要答疑辅导欢迎私VX: code5bug

华为OD机试真题

题目描述

给你一个字符串 str和整数k,返回满足以下条件的所有子字符串个数:

  1. 恰好包含k 个字母
  2. 数字0-9各出现至少一次

输入描述

第一行为字符串 str (1 <= length <= 100000),仅包含数字和小写字母。

第二行为整数k(0 <= k <= 100000)

输出描述

输出一个整数,表示满足条件的所有子字符串个数

补充说明

子字符串 是字符串中连续的 非空 字符序列

示例1

输入:

a0123456789aa
1

输出:

2

示例2

输入:

aa01234a5678901234aa
1

输出:

22

题解

  1. 滑动窗口法:使用两个滑动窗口来维护满足条件的子字符串。
    1. 窗口1(left1):指向满足恰好k个字母且所有数字0-9都出现至少一次的最早左边界。
    2. 窗口2(left2):指向不满足恰好k个字母且所有数字0-9都出现至少一次的最早左边界。
  2. 双指针维护:使用两个指针left1left2来分别维护窗口1和窗口2的左边界。
  3. 统计数字频率:使用哈希表(或字典)来统计当前窗口中数字的出现频率,确保所有数字0-9都至少出现一次。
  4. 计算有效子字符串:对于每个右指针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();
})();

希望这个专栏能让您熟练掌握算法, 🎁🎁🎁。

整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

什码情况

你的鼓励就是我最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值