【LeetCode】剑指 Offer Ⅱ 第3章:字符串(7道题) -- Java Version
题库链接:https://leetcode.cn/problem-list/e8X3pBZi/
题目 | 解决方案 |
---|---|
剑指 Offer II 014. 字符串中的变位词 | 双指针 + 数组模拟哈希表 ⭐ |
剑指 Offer II 015. 找到字符串中所有字母异位词 | 双指针 + 数组模拟哈希表 ⭐ |
剑指 Offer II 016. 不含重复字符的最长子字符串 | 双指针 + 数组模拟哈希表 ⭐ |
剑指 Offer II 017. 最小覆盖子串 | 双指针 + 哈希表 ⭐ |
剑指 Offer II 018. 有效的回文 | 双指针(前后)⭐ |
剑指 Offer II 019. 最多删除一个字符得到回文 | 双指针(前后)⭐ |
剑指 Offer II 020. 回文子字符串的个数 | 双指针(从回文中心出发)⭐ |
本章的题目主要分为两种类型:变位词 和 回文;
……
如果将 字符串 看成一个由字符组成的数组,那么也可以用两个指针来定位一个子字符串,其中一个指针指向字符串的第1个字符,另一个指针指向字符串的最后一个字符,两个指针之间所包含的就是一个子字符串。
……
一般来说,就双指针的字符串问题而言,这种类型的面试题基本上都与 统计字母出现的次数 有关,我们常用 哈希表 来存储每个元素出现的次数。
1. 剑指 Offer II 014. 字符串中的变位词 – P31
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。
1.1 滑动窗口 – O(m+n)
时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)
窗口定长为 n,每次向前移动一格.
class Solution {// 1. 滑动窗口public boolean checkInclusion(String s1, String s2) {int n = s1.length(), m = s2.length();if (n > m) return false; // 如果 n > m,则 s2 中必不存在 s1 的变位词int[] cnt1 = new int[26]; // 26个英文字母int[] cnt2 = new int[26];for (int i = 0; i < n; i++) {++cnt1[s1.charAt(i) - 'a'];++cnt2[s2.charAt(i) - 'a'];}if (Arrays.equals(cnt1, cnt2)) return true; for (int i = n; i < m; i++) {++cnt2[s2.charAt(i) - 'a'];--cnt2[s2.charAt(i-n) - 'a'];if (Arrays.equals(cnt1, cnt2)) return true;}return false;}
}
1.2 双指针 + 数组模拟哈希表 – O(m+n)(⭐)
时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)
class Solution {// 2. 双指针 + 数组模拟哈希表public boolean checkInclusion(String s1, String s2) {int n = s1.length(), m = s2.length();if (n > m) {return false;}int[] cnt = new int[26];for (int i = 0; i < n; i++) --cnt[s1.charAt(i) - 'a']; // 让 s1 全为负数for (int l = 0, r = 0; r < m; r++) { // 双指针遍历 s2,从 s2 中找出 s1 的变式int t = s2.charAt(r) - 'a';cnt[t]++;while (cnt[t] > 0) --cnt[s2.charAt(l++) - 'a'];if (r - l + 1 == n) return true; // 说明此时 cnt 数组之和全为 0}return false;}
}
2. 剑指 Offer II 015. 找到字符串中所有字母异位词 – P35
给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
……
此题也可使用1题中 滑动窗口 的解法,但是滑动窗口 + 两个 cnt[] 的方法要比双指针的慢一些,为避免冗余,此处省略.
2.1 双指针 + 数组模拟哈希表 – O(m+n)(⭐)
时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)
class Solution {// 双指针public List<Integer> findAnagrams(String s, String p) {int n = s.length(), m = p.length();List<Integer> list = new LinkedList<>();int[] cnt = new int[26]; // 26个小写英文字母for (int i = 0; i < m; i++) --cnt[p.charAt(i) - 'a']; // 初始化 p 串, 使cnt--for (int l = 0, r = 0; r < n; r++) { // 双指针移动 s 串int t = s.charAt(r) - 'a';cnt[t]++;while (cnt[t] > 0) --cnt[s.charAt(l++) - 'a'];if (r - l + 1 == m) list.add(l); // 在 s 中找到 p 的变位词,记录起始索引} return list;}
}
3. 剑指 Offer II 016. 不含重复字符的最长子字符串 – P36
给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。
3.1 双指针 + 数组模拟哈希表 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
class Solution {// 1. 双指针 + 数组模拟哈希表public int lengthOfLongestSubstring(String s) {int n = s.length();if (n == 0) return 0;int res = 0;int[] ascii = new int[256]; // 与前两题不同的是,这次不局限于小写英文字母了for (int l = 0, r = 0; r < n; r++) {int t = s.charAt(r);ascii[t]++;while (ascii[t] > 1) --ascii[s.charAt(l++)]; // ascii[i] > 0, 说明出现了重复字符,开始移动 l res = Math.max(res, r - l + 1); // 记录不重复子字符串的长度}return res;}
}
3.2 HashSet – O(n)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合,记录每个字符是否出现过Set<Character> set = new HashSet<Character>();int n = s.length();// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动int r = -1, ans = 0;for (int l = 0; l < n; l++) {if (l != 0) {// 左指针向右移动一格,移除一个字符set.remove(s.charAt(l - 1));}while (r + 1 < n && !set.contains(s.charAt(r + 1))) {// 不断地移动右指针set.add(s.charAt(r + 1));++r;}// 第 l 到 r 个字符是一个极长的无重复字符子串ans = Math.max(ans, r - l + 1);}return ans;}
}
PS:补充知识1 - 【HashSet】
💡 HashSet集合(元素无序且唯一),其底层是哈希表,特点:
- 无序;
- 不可重复;
- 不能排序;
……
元素的唯一性是靠元素重写 hashCode() 和 equals() 方法来保证的,如果不重写则无法保证唯一性.(Integer以及String类等都已经重写了这两个方法,一定要注意自己定义的类需要重写才能确保唯一性!)
……
更多内容可参考:
[1] 单列集合Set的实现类HashSet - 陪雨岁岁年年的博客
[2] Java-单列集合(Collection,List,Set集合的详细介绍)- 壹万捌先生的博客
4. 剑指 Offer II 017. 最小覆盖子串 – P39
给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。
4.1 双指针 + 哈希表 – O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
class Solution {public String minWindow(String s, String t) {int n = s.length(), m = t.length();Map<Character,Integer> tmap = new HashMap<>();for (int i = 0; i < m; i++) tmap.put(t.charAt(i), tmap.getOrDefault(t.charAt(i),0)+1);int count = 0;Map<Character,Integer> smap = new HashMap<>();String res = "";for (int l = 0, r = 0; r < n; r++) {char rch = s.charAt(r);smap.put(rch, smap.getOrDefault(rch,0)+1);if (smap.get(rch) <= tmap.getOrDefault(rch,0)) { // 排除重复冗余字符count++;}while (smap.getOrDefault(s.charAt(l),0) > tmap.getOrDefault(s.charAt(l),0) && l < r) { // 当子字符串超过所需匹配字符时,移动lsmap.put(s.charAt(l), smap.getOrDefault(s.charAt(l),0)-1);l++;}if (count == m) {if (res.isEmpty() || (r - l + 1) < res.length()){ // 比较找出最小字符串res = s.substring(l, r + 1);}}}return res;}
}
4.2 双指针 + 数组模拟哈希表 – O(n)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
相比于4.1要更快,但是相对也要更难记忆.
class Solution {public String minWindow(String s, String t) {int[] map = new int[256];for(char c: t.toCharArray()){ // 初始化 t 字符串map[c]++;}int minLen = Integer.MAX_VALUE, minStart = 0; // 设定最短字符串的长度和起始位置int m = t.length(), n = s.length();char[] sc = s.toCharArray();for (int l = 0, r = 0; r < n; r++){int rch = sc[r];map[rch]--;if(map[rch] >= 0){ // 减小与 t 的字符差距m--; }if(m == 0){ // 成功在 s 中匹配到包含 t 的子字符串int lch = sc[l];while(map[lch] < 0){ // 开始尝试缩小窗口范围map[lch]++;l++;lch = sc[l]; }int len = r - l + 1;if(len < minLen){ // 找出最小子字符串minLen = len;minStart = l;}map[lch]++;l++;m++;}}return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);}
}
5. 剑指 Offer II 018. 有效的回文 – P42
给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。
5.1 双指针(前后)-- O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
class Solution {// 双指针:需要注意的是,有效字符包括:英文字母、数字和空格public boolean isPalindrome(String s) {int n = s.length();char[] sch = s.toLowerCase().toCharArray();for (int l = 0, r = n-1; l < r;) {if (!Character.isLetterOrDigit(sch[l])) l++; // 判断是否为英文字母或数字else if (!Character.isLetterOrDigit(sch[r])) r--;else {if (sch[l] != sch[r]) return false; // 前后指针不相同,说明非回文l++;r--;}}return true;}
}
PS:补充知识1 - 【Character包装类 – 8种方法】
判断一个字符是否是数字、英文、其他字符.
// 1. 判断该字符是否是字母,是返回true,否返回false
Character.isLetter(char ch)
// 2. 判断一个字符是否是数字字符,是返回true,否返回false
Character.isDigit(char ch)
// 3. 判断该字符是字母还是数字,如果字符是字母或数字返回true; 否则false。
Character.isLetterOrDigit(char ch)
// 4. 判断指定字符是否为空白字符,空白符包含:空格、tab 键、换行符
Character.isWhitespace(char ch)
// 5. 判断该字符是否是小写字母,是返回true,否返回false
Character.isLowerCase(char ch)
// 6. 判断该字符是否是大写字母,是返回true,否返回false
Character.isUpperCase(char ch)
// 7. 将小写字符转换为大写
Character.toUpperCase('A')
// 8. 将大写字符转换为小写
Character.toUpperCase('a')
// 9. 用于返回一个表示指定 char 值的 String 对象。结果是长度为 1 的字符串,仅由指定的 char 组成。
Character.toString('a')
6. 剑指 Offer II 019. 最多删除一个字符得到回文 – P43
给定一个非空字符串 s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。
6.1 双指针(前后)-- O(n)(⭐)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
class Solution {public boolean validPalindrome(String s) {int n = s.length();char[] sch = s.toCharArray();int l = 0, r = n-1;for (; l < r; l++, r--) {if (sch[l] != sch[r]) break; // 找到第一次不同之处}return isPalindrome(l+1,r,sch) || isPalindrome(l,r-1,sch); // 删左或删右}public boolean isPalindrome(int l, int r, char[] sch) { // 再次利用双指针进行回文判断while (l < r) {if (sch[l] != sch[r]) return false;l++;r--;}return true;}
}
6.2 双指针(前后)+StringBuilder – O(n)
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
速度并不会提升,且书写代码量似乎也不会渐少,总之不太推荐这种写法。
class Solution {public boolean validPalindrome(String s) {int n = s.length();int l = 0, r = n - 1;while (l < r) {if (s.charAt(l) != s.charAt(r)) break;l++;r--;}if (r <= l) return true;String ls = s.substring(l+1, r+1); // substring(), 左闭右开StringBuilder stringBuilder = new StringBuilder(ls);String rs = s.substring(l, r);StringBuilder stringBuilder2 = new StringBuilder(rs);return ls.equals(stringBuilder.reverse().toString()) || rs.equals(stringBuilder2.reverse().toString());}
}
PS:补充知识1 - 【substring() 方法】
substring() 方法返回字符串的子字符串,左闭右开区间 [beginIndex, endIndex)。
public class RunoobTest {public static void main(String args[]) {String Str = new String("This is text");System.out.print("返回值 :" );System.out.println(Str.substring(4) ); // 返回值 : is textSystem.out.print("返回值 :" );System.out.println(Str.substring(4, 10) ); // 返回值 : is te}
}
7. 剑指 Offer II 020. 回文子字符串的个数 – P44
给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。
7.1 双指针(从回文中心出发)-- O(n2)(⭐)
时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)
class Solution {// 双指针:从回文中心出发public int countSubstrings(String s) {int n = s.length();if (n <= 1) return n;int count = 0;char[] sch = s.toCharArray();for (int i = 0; i < n; i++) {count += countPalindrome(sch, i, i); // 奇数长度:一个对称中心count += countPalindrome(sch, i, i+1); // 偶数长度:两个对称中心}return count;}public int countPalindrome(char[] sch, int l, int r) {int count = 0;while (l >= 0 && r < sch.length && sch[l] == sch[r]) {count++;l--;r++;}return count;}
}
相关文章:

【LeetCode】剑指 Offer Ⅱ 第3章:字符串(7道题) -- Java Version
题库链接:https://leetcode.cn/problem-list/e8X3pBZi/ 题目解决方案剑指 Offer II 014. 字符串中的变位词双指针 数组模拟哈希表 ⭐剑指 Offer II 015. 找到字符串中所有字母异位词双指针 数组模拟哈希表 ⭐剑指 Offer II 016. 不含重复字符的最长子字符串双指针…...

【python】绘图代码模板
【python】绘图代码模板 pandas.DataFrame.plot( )画图函数Seaborn绘图 -数据可视化必备主题样式导入数据集可视化统计关系散点图抖动图箱线图小提琴图Pointplot群图 可视化数据集的分布绘制单变量分布柱状图直方图 绘制双变量分布Hex图KDE 图可视化数据集中的成对关系 好看的图…...

RTT学习笔记12-KConfig 语法学习
KConfig 语法学习 RTT 官方教程 https://www.rt-thread.org/document/site/#/development-tools/build-config-system/Kconfig 我自己写的IIC配置 menuconfig BSP_USING_I2C # I2C 菜单bool "Enable I2C BUS" # 提示I2C 菜单default n # 默认不使能I2C 菜单…...

基于Mediapipe的姿势识别并同步到Unity人体模型中
如题,由于是商业项目,无法公开源码,这里主要说一下实现此功能的思路。 人体关节点识别 基于Mediapipe Unity插件进行开发,性能比较低的CPU主机,无法流畅地运行Mediapipe,这个要注意一下。 Mediapipe33个人体…...

Linux下进程的特点与环境变量
目录 进程的特点 进程特点的介绍 进程时如何实现并发性的 进程间如何切换 概念铺设 PC指针 上下文 环境变量 PATH 修改PATH HOME SHELL env 命令行参数 什么是命令行参数? 打印命令行参数 通过函数获得环境变量 getenv 命令行参数 env 修改环境变…...
以Llama-2为例,在生成模型中使用自定义LogitsProcessor
以Llama-2为例,在生成模型中使用自定义LogitsProcessor 1. 前言2. 场景介绍3. 解决方法4. 结语 1. 前言 在上一篇文章 以Llama-2为例,在生成模型中使用自定义StoppingCriteria中,介绍了怎样在生成的过程中,使用stopping criteria…...
python 计算图片hash 缓存图片为key
python,有时希望缓存图片作为key,怎么办?缓存整张突破占用内存太多,不妨缓存hash值: Fast way to Hash Numpy objects for Caching import hashlib import numpy a numpy.random.rand(10, 100) b a.view(numpy.uin…...

制造型企业如何实现车间设备生产数据的实时采集?需要5G网络吗?
引言 在制造业数字化转型的浪潮下,实时采集车间设备生产数据变得尤为重要。工业边缘网关HiWoo Box作为一款专为工业应用而设计的智能设备,具备工业级设计和多种联网方式,为制造型企业提供了高性能的车间设备数据实时采集解决方案。本文将重点…...
第2章 HTML中的JavaScript
引言 将JavaScript引入网页,首先要解决它与网页的主导语言HTML的关系问题 script元素 将JavaScript插入HTML的主要方法是使用script元素,script有8个可选属性 async:表示异步加载js文件内容,他们之间的顺序不一定按照html顺序ch…...

景联文科技高质量成品数据集上新啦!
景联文科技近期上新多个成品数据集,包含图像、视频等多种类型的数据,涵盖丰富的场景,可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型,使得模型能够更加全面和精准地理解和处理任务,更好地应对复…...
flask------请求拓展
flask中也有类似与django中的中间件,只不过是另一种写法,但是他们的作用是一样的,下面我们就一一介绍: 1.before_request 作用 : before_request 相当于 django 中的 process_request,每一个请求在被处理前都会经…...
大数据-玩转数据-FLINK-从kafka消费数据
一、基于前面kafka部署 大数据-玩转数据-Kafka安装 二、FLINK中编写代码 package com.lyh.flink04;import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apa…...

介绍Sping Boot的5个扩展点
1、初始化器ApplicationContextInitializer 我们在启动Spring Boot项目的时候,是执行这样一个方法来启动的 我们一层一层往下点,最终发现执行的是这个方法 所以我们在启动项目的时候也可以这样启动 new SpringApplication(SpringbootExtensionPointAp…...
Linux2.6内核配置说明
maturity level options代码成熟度选项 Prompt for development and/or incomplete code/drivers 显示尚在开发中或尚未完成的代码与驱动.除非你是测试人员或者开发者,否则请勿选择 setup常规设置 Local version - append to kernel release 在内核版本后面加上自定义的…...

Pytest简介及jenkins集成
一、pytest介绍 pytest介绍 - unittest\nose pytest:基于unittest之上的单元测试框架 自动发现测试模块和测试方法 断言使用assert表达式即可 可以设置测试会话级、模块级、类级、函数级的fixtures 数据准备 清理工作 unittest:setUp、teardown、…...

【LeetCode】105. 从前序与中序遍历序列构造二叉树 106. 从中序与后序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树 这道题也是经典的数据结构题了,有时候面试题也会遇到,已知前序与中序的遍历序列,由前序遍历我们可以知道第一个元素就是根节点,而中序遍历的特点就是根节点的左边全部为左子树,右…...

堆内存和一些检测工具
17 堆定义 通过new关键字创建,创建对象都会使用堆内存。 是线程共享的,需要考虑线程安全问题。 有垃圾回收机制。18 堆-内存溢出 当默认情况下,发现执行到26,出现内存溢出。 当我们将堆内存调为8m,继续执行ÿ…...
【JavaScript】元素获取指南
简介 在 JavaScript 中,我们经常需要通过获取元素来进行 DOM 操作和交互。本教程将介绍多种获取元素的方式,包括根据 ID、标签名、类名、选择器、属性和名称等。 通过ID获取元素 使用getElementById方法根据元素的ID属性获取单个元素。 var element = document.getElementB…...

uniapp 返回上一页并刷新
如要刷新的是mine页面 在/pages/mine/improveInfo页面修改信息,点击保存后跳转到个人中心(/pages/mine/index)页面并刷新更新数据 点击保存按钮时执行以下代码: wx.switchTab({url: /pages/mine/index }) // 页面重载 let pages …...

Java阶段五Day21
Java阶段五Day21 文章目录 Java阶段五Day21问题解析rocketmq清空数据 linux学习背景什么是linux系统虚拟机介绍启动 虚拟机linux虚拟机网络的问题 linux系统的基础命令命令提示符命令格式pwd指令ls指令cd指令mkdirtouch指令cp指令rm指令mv指令cat指令tail指令 文本编辑器vim操作…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...