LeetCode 1461. 检查一个字符串是否包含所有长度为 K 的二进制子串

LeetCode 1461. 检查一个字符串是否包含所有长度为 K 的二进制子串

题目描述

给你一个二进制字符串 s 和一个整数 k。如果所有长度为 k 的二进制字符串都是 s 的子串,请返回 true,否则返回 false

示例
输入:s = “00110110”, k = 2
输出:true
解释:长度为 2 的二进制串有 “00”、“01”、“10”、“11”,它们都在 s 中出现过。

解法一:哈希集合存储子串

思路

长度为 k 的二进制子串一共有 2^k 种可能。我们只需要将字符串 s 中所有长度为 k 的子串存入一个哈希集合,最后判断集合大小是否等于 2^k 即可。

剪枝优化

如果字符串的长度连理论上的最小长度都不够,那么肯定无法包含所有子串。包含所有 2^k 个二进制串的最短字符串长度是 (1 << k) + k - 1(即 de Bruijn 序列的长度)。因此可以先检查长度,快速返回 false

代码实现

classSolution{public:boolhasAllCodes(string s,int k){// 长度预判断if(s.size()<(1<< k)+ k -1)returnfalse; unordered_set<string> strs;for(int i =0; i + k <= s.size();++i){// 截取长度为 k 的子串并插入集合 strs.insert(s.substr(i, k));}return strs.size()==(1<< k);}};

复杂度分析

  • 时间复杂度:O(n·k),其中 n 是字符串长度。需要遍历 O(n) 个子串,每次截取子串和计算哈希需要 O(k) 时间。
  • 空间复杂度:O(2^k·k),哈希集合中最多存储 2^k 个长度为 k 的字符串。

解法二:滑动窗口 + 位运算

思路

将长度为 k 的二进制子串视为一个二进制整数,取值范围为 [0, 2^k - 1]。我们可以通过滑动窗口在 O(1) 时间内计算出每个子串对应的整数值,并存入哈希集合,最后判断集合大小是否为 2^k

滑动窗口更新公式

假设当前窗口对应的整数值为 num(窗口从下标 i 开始)。当窗口向右滑动一位时,新的窗口值为:

new_num = (old_num - (leftmost << (k-1))) * 2 + rightmost 

其中:

  • leftmost 为移出窗口的最左边字符(转换为 0 或 1)
  • rightmost 为新移入窗口的最右边字符

具体推导:

  • 旧窗口值 num 可以表示为:leftmost * 2^(k-1) + middle,其中 middle 是后 k-1 位的数值。
  • 去掉最高位后的 middle = num - (leftmost << (k-1))
  • middle 左移一位(乘以 2)得到 middle * 2,相当于后 k-1 位整体左移,低位补 0。
  • 加上新移入的 rightmost,得到新窗口值。

代码实现

classSolution{public:boolhasAllCodes(string s,int k){// 长度预判断if(s.size()<(1<< k)+ k -1)returnfalse;// 将前 k 个字符转换为整数int num =stoi(s.substr(0, k),nullptr,2); unordered_set<int> exists ={num};for(int i =1; i + k <= s.size();++i){// 滑动窗口更新数值 num =(num -((s[i -1]-'0')<<(k -1)))*2+(s[i + k -1]-'0'); exists.insert(num);}return exists.size()==(1<< k);}};

复杂度分析

  • 时间复杂度:O(n)。每次窗口滑动只进行常数次位运算和集合插入,总时间复杂度为 O(n)。
  • 空间复杂度:O(2^k)。使用整数集合存储所有可能的数值,每个整数占 4 字节,比存储字符串更节省空间。

两种解法对比

方法时间复杂度空间复杂度适用场景
哈希集合(子串)O(n·k)O(2^k·k)代码简单,适合 k 较小的情况
滑动窗口+位运算O(n)O(2^k)效率更高,k 较大时不易超时

总结:解法二利用位运算将子串映射为整数,避免了字符串操作,是更优的实现。但两种方法的核心思想相同:统计所有长度为 k 的不同子串,并与总数 2^k 比较。


扩展思考

  • k 较大时(如 k > 20),2^k 可能超过内存限制,但题目通常保证 k ≤ 20,所以可行。
  • 解法一中 s.substr(i, k) 本身返回临时对象,会自动触发移动语义,无需显式使用 move

Read more

HarmonyOS6半年磨一剑 - RcImage组件填充模式与形状系统设计(一)

HarmonyOS6半年磨一剑 - RcImage组件填充模式与形状系统设计(一)

目录 * 前言 * 项目简介 * 核心特性 * 开源计划 * rchoui官网 * 文档概述 * 第一章: 填充模式系统 * 1.1 填充模式类型定义 * 1.2 填充模式对比分析 * 1.3 填充模式实现机制 * 第二章: contain 模式深度解析 * 2.1 contain 模式工作原理 * 2.2 contain 模式适用场景 * 第三章: cover 模式深度解析 * 3.1 cover 模式工作原理 * 3.2 cover 模式适用场景 * 第四章: fill 模式深度解析 * 4.1 fill 模式工作原理 * 4.2 fill

By Ne0inhk
大力学习台灯T6/T6Pro 救砖实战:macOS/Windows 用 mtkclient 从 Fastboot 无限重启完整恢复(含固件下载地址)

大力学习台灯T6/T6Pro 救砖实战:macOS/Windows 用 mtkclient 从 Fastboot 无限重启完整恢复(含固件下载地址)

大力学习台灯T6/T6Pro(MTK)救砖实战(小白可用):macOS/Windows 用 mtkclient 从 Fastboot/Logo 无限重启完整恢复(含恢复原机 SN/proinfo) 本文记录一次 Dali T6 学习机(联发科 MTK 平台,示例识别为 MT6771/0x788 系列)从“卡 Fastboot / Logo 无限重启”到 成功进入系统,并最终 恢复原机 SN/设备身份(proinfo) 的完整过程。 如果你是小白:你只需要按本文顺序复制粘贴命令即可。每一步我都写了: TL;DR(傻瓜式总流程:照抄就能修) 下面这套是“最短路径”修复流程:

By Ne0inhk
Flutter 三方库 galileo_mysql 的鸿蒙化适配指南 - 支持 MySQL 8.0 协议、高性能长连接与异步事务处理

Flutter 三方库 galileo_mysql 的鸿蒙化适配指南 - 支持 MySQL 8.0 协议、高性能长连接与异步事务处理

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 galileo_mysql 的鸿蒙化适配指南 - 支持 MySQL 8.0 协议、高性能长连接与异步事务处理 前言 在 Flutter for OpenHarmony 的应用开发中,直接在端侧进行数据库操作虽然不是主流(通常通过 API),但在某些边缘计算或内网工具类场景下,直接连接 MySQL 数据库依然是刚需。galileo_mysql 作为一个纯 Dart 实现的 MySQL 驱动,其天然的跨平台属性使其成为鸿蒙端直接操作 MySQL 的首选。本文将详细介绍如何在 OpenHarmony 环境下适配并使用该库。 一、原理解析 / 概念介绍 1.1 基础原理 galileo_

By Ne0inhk

Flutter 三方库 sync_http 的鸿蒙化适配指南 - 掌控同步网络请求、底层脚本通讯实战、鸿蒙级工具开发专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 sync_http 的鸿蒙化适配指南 - 掌控同步网络请求、底层脚本通讯实战、鸿蒙级工具开发专家 在鸿蒙跨平台应用开发中,虽然绝大多数场景都提倡异步处理,但在某些特定的底层工具开发、初始化脚本或极其简易的命令行工具(CLI)中,我们需要一种简单、直接的同步(Synchronous)HTTP 请求能力。如果你追求的是那种“发请求、等结果、再继续”的线性逻辑。今天我们要深度解析的 sync_http——一个专门为同步阻塞式网络交互设计的 Dart 库,正是帮你实现“确定性通讯”的差异化神器。 前言 sync_http 是 Dart 标准库中被广泛引用的同步 HTTP 实现。它不使用 Future 或

By Ne0inhk