【力扣算法日记】无重复字符的最长子串

最近刷了很多算法题,这些解题过程也拓展了自己的思路,是个适合记录的素材。所以决定在继技术知识点详解的【一文系列】之后,开启新坑——【力扣算法系列】,来记录力扣刷题过程。

分享题目不确定,目前打算只分享我认为值得分享的题目,或者你们有什么想要我分享的题目,我愿意去研究透彻之后出分析详解!分享的过程也是自我学习的过程!

leetCode

那么今天,我们分享第一道题目:无重复字符的最长子串

题目

无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

来源:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成


解题

暴力法

这是最容易想到的方法,当然也是最废的方法。不过我们在解题的时候,可以先尝试暴力解法,然后再一步步着手优化,或想出更好的方法,这也是一种解题思路。在这道题目中,暴力解法就是逐个检查所有的子字符串,看它是否不含有重复的字符。

public static int lengthOfLongestSubstring(String s) {char[] chars = s.toCharArray();Map<Character, Integer> subChars = new HashMap<Character, Integer>(chars.length);int maxSize = 0;int repeatIndex = -1;int i = 0;while (i < chars.length) {for (int j = i; j < chars.length; j++) {char c = chars[j];if (subChars.containsKey(c)) {repeatIndex = subChars.get(c);break;}subChars.put(c, j);}if (repeatIndex != -1) {// 找到重复元素,从第一个重复元素的下一个下标开始遍历i = repeatIndex + 1;repeatIndex = -1;} else {i++;}maxSize = Math.max(maxSize, subChars.size());subChars.clear();float middleSize = chars.length / 2f;if (maxSize >= middleSize && i >= middleSize) {// 最大子串长度超过一半了,可以不用继续遍历了break;}}return maxSize;
}

滑动窗口

滑动窗口是字符串类问题的最常用的方法。在这道题目中,我们按区间进行字符串搜索,遇到重复字符,就从左区间开始删除,直到删除掉重复字符为止。

在这道题目中,滑动窗口其实相当于就是一个队列,比如例题中的 abcabcbb:

  1. 进入这个队列(窗口)为 abc,暂时满足题目要求
    滑动窗口

  2. 当再进入 a,队列变成了 abca
    滑动窗口

  3. 这时候不满足要求,所以,我们要移动这个队列,把队列的左边的元素移出就行了,一直到满足题目要求。
    滑动窗口

一直维持这样的队列,找出队列出现最长的长度时候,我们就求得最终答案。

public int lengthOfLongestSubstring(String s) {int len = s.length();// 最大长度int maxLength = 0;// 左指针int left = 0;// 右指针int right = 0;// 子串队列Set<Character> subChars = new HashSet<>();while (left < len && right < len) {if (subChars.contains(s.charAt(right))) {// 子串重复,字符串从队首开始移除subChars.remove(s.charAt(left));// 移动左指针left++;} else {// 子串不重复,字符入队尾subChars.add(s.charAt(right));right++;// 记录最长不重复长度maxLength = Math.max(maxLength, right - left);}}return maxLength;
}

优化的滑动窗口

在滑动窗口时,上述的方法最多需要执行 2n 个步骤。事实上,它可以被进一步优化为仅需要 n 个步骤。我们可以定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口。

public static int lengthOfLongestSubstring(String s) {int len = s.length();// 最大长度int maxLength = 0;// 使用Map结构缓存字符和索引的映射Map<Character, Integer> map = new HashMap<>();// 滑动窗口for (int right = 0, left = 0; right < len; right++) {char c = s.charAt(right);if (map.containsKey(c)) {// 找到重复子串,左指针跳到最后的重复位置left = Math.max(map.get(c), left);}int curLength = right - left + 1;// 取最大长度maxLength = Math.max(maxLength, curLength);// 缓存字符和下标映射map.put(c, right + 1);}return maxLength;
}

优化的滑动窗口2

针对上述优化方式,我们还可以进一步优化,不过这个方法相对有些投机取巧,各位看看就行

public static int lengthOfLongestSubstring(String s) {
//      Java(假设字符集为 ASCII 128)
//      以前我们没有对字符串 s 所使用的字符集进行假设。
//      当我们知道该字符集比较小的时侯,我们可以用一个整数数组作为直接访问表来替换 Map。int len = s.length();// 最大长度int maxLength = 0;// 使用128位数组缓存下标int[] index = new int[128];// 滑动窗口for (int right = 0, left = 0; right < len; right++) {char c = s.charAt(right);// 取index对应字符的缓存下标// 如果没有,则是默认值0// 若一旦有非零值,则代表有相同字符缓存到index数组中,则下标可跳到最后重复为止,再计算出不重复长度left = Math.max(index[c], left);int curLength = right - left + 1;// 取最大长度maxLength = Math.max(maxLength, curLength);// 缓存字符和下标映射index[c] = right + 1;}return maxLength;
}

这道题的解法中,暴力解法一定是不可取的,其实能写出滑动窗口解法就差不多了,优化方式都是加分项了。当然了,解题方法一定不止于此,大家可以继续深究!


更多技术干货,欢迎关注我!

qr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/325064.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# OpenCvSharp DNN FreeYOLO 人脸检测

目录 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN FreeYOLO 人脸检测 效果 模型信息 Inputs ------------------------- name&#xff1a;input tensor&#xff1a;Float[1, 3, 192, 320] --------------------------------------------------------------- Outp…

基于SSM的人事档案管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

C ++类

定义一个Person类&#xff0c;私有成员int age&#xff0c;string &name&#xff0c;定义一个Stu类&#xff0c;包含私有成员double *score&#xff0c;写出两个类的构造函数、析构函数、拷贝构造和拷贝赋值函数&#xff0c;完成对Person的运算符重载(算术运算符、条件运算…

canvas设置图形的阴影

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

微信小程序:flex布局实现换行

1、关键代码.wxml&#xff1a; <view class"pay margin-top-40"><view class"info"><view class"pay-info-title margin-left-22 flex-start"> 请选择充值金额</view><view class"flex-wrap margin-top-20&quo…

如何在 Ubuntu 20.04 上安装和使用 Docker

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 20.04 上安装和使用 Docker 介绍 Docker是一个可以简化容器中应用程序进程管理过程的应用程序。…

数字后端设计实现 | 数字后端PR工具Innovus中如何创建不同高度的row?

吾爱IC社区星球学员问题&#xff1a;Innovus后端实现时两种种不同高度的site能做在一个pr里面吗&#xff1f; 答案是可以的。 Innovus支持在同一个设计中中使用不同的row&#xff0c;但需要给各自子模块创建power domain。这里所说的不同高度的row&#xff0c;有两种情况。 1…

基础面试题整理1

1.面向对象的特点 继承&#xff08;复用性&#xff09;、封装&#xff08;复用性&#xff09;、多态&#xff08;可移植性、灵活性&#xff09; 2.ArrayList与LinkedList区别 ArrayList和LinkedList都是实现了List接口 ArrayList底层是动态数组 LinkedList底层是链表&#…

【EI会议征稿通知】2024年人工智能与电力系统国际学术会议(AIPS 2024)

2024年人工智能与电力系统国际学术会议&#xff08;AIPS 2024&#xff09; 2024 International Conference on Artificial Intelligence and Power System 2024年人工智能与电力系统国际学术会议 (AIPS 2024) 将于2024年04月19日-21日在中国成都召开。AIPS 2024将围绕“人工智…

Python实现某城市从站点API获取天气状况示例(Crossin教室实例24)

一、要点说明&#xff1a; 根据站点当前API数据是由‘\r’字符连接的字符串的特点&#xff0c;主要用到了字符串的split()方法。此方法参数就是‘\r’。函数返回值是被分隔的字符串的列表。通过使用列表索引就可以分项取到天气数据。 二、示例代码&#xff1a; import reque…

操作系统期末复习知识点

目录 一.概论 1.操作系统的介绍 2.特性 3.主要功能 4.作用 二.进程的描述与控制 1.进程的定义 2.特性 3.进程的创建步骤 4.基本状态转化 5.PCB的作用 6.进程与线程的比较 三.进程同步 1.同步的概念&#xff08;挺重要的&#xff09; 2.临界区 3.管程和进程的区…

Spring Boot 整合 Caffeine 本地缓存及 Spring Cache 注解的使用

Spring Boot 整合 Caffeine 本地缓存及 Spring Cache 注解的使用 介绍 在现代的Web应用程序中&#xff0c;缓存是提高性能和响应速度的重要手段之一。Spring Boot提供了对缓存的良好支持&#xff0c;并且可以轻松地整合Caffeine本地缓存作为缓存提供者。结合Spring Cache注解…