【LeetCode题解】3_无重复字符的最长子串(Longest-Substring-Without-Repeating-Characters)
更多 LeetCode 题解笔记可以访问我的 github。
描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解法一:暴力枚举法(Time Limit Exceeded)
思路
这种方法采用的思路是:列举出字符串中所有的子串,然后判断字串是否不包含重复字符,如果是,则将该子串的长度与当前保存的最长长度(用一个变量存储)进行比较,保留二者的大者。遍历完所有的子串后,将可以得到不包含重复元素的最长子串的长度。
Java 实现
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
if (allUnique(s, i, j)) {
ans = Math.max(ans, j - i);
}
}
}
return ans;
}
private boolean allUnique(String s, int start, int end) {
Set<Character> set = new HashSet<>();
for (int i = start; i < end; ++i) {
Character ch = s.charAt(i);
if (set.contains(ch)) {
return false;
}
set.add(ch);
}
return true;
}
}
Python 实现
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
def is_unique(s, start, end):
ch_has_seen = set()
for c in s[start:end]:
if c in ch_has_seen:
return False
else:
ch_has_seen.add(c)
return True
ans = 0
for i in range(len(s)):
for j in range(1, len(s) + 1):
if is_unique(s, i, j):
ans = max(j - i, ans)
return ans
复杂度分析
- 时间复杂度:\(O(n^3)\),其中,\(n\) 表示字符串的长度。对于每个字符串中的每个子串,需要 \(j -i\) 次操作才能判断该子串是否不包含重复字符(遍历子串的每个字符),因此,总的操作次数为 \(\sum_{i=0}^{n-1}\sum_{j=i+1}^{n} (j-i) = \sum_{i=0}^{n} \frac{(n - i -+1)(n - i)}{2}\),即时间复杂度为 \(O(n^3)\)
- 空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目。方法中需要占用 \(O(k)\) 大小的空间用于存储看过的字符集(\(k\) 表示集合的大小),而 \(k\) 的上界主要取决于字符串的长度 \(n\) 和字符集中字符的数目 \(m\)
解法二:滑动窗口(双指针)
思路
解法二借助于一个大小可变的滑动窗口。当窗口中不存在重复字符时,窗口的右边界向右滑动(增加窗口中的字符数),如果窗口中存在重复字符,则将窗口的左边界向右滑动,减少窗口中的字符数。并且,每次增加新的字符时,都与当前最长子串的长度进行比较,保留二者的大者。当窗口的右边界抵达字符串的末尾时,遍历结束,返回保存的最长子串的长度。滑动窗口可以采用集合进行表示。
Java 实现
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0, l = 0, r = 0;
Set<Character> set = new HashSet<>();
while (l < n && r < n) {
if (!set.contains(s.charAt(r))) {
set.add(s.charAt(r++));
ans = Math.max(r - l, ans);
} else {
set.remove(s.charAt(l++));
}
}
return ans;
}
}
Python 实现
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
l, r, max_len = 0, 0, 0
substring = set()
while l < len(s) and r < len(s):
if s[r] in substring:
substring.remove(s[l])
l += 1
else:
substring.add(s[r])
r += 1
max_len = max(r - l, max_len)
return max_len
复杂度分析
- 时间复杂度:\(O(n)\),其中 \(n\) 表示字符串的长度。最坏情况下(例如:字符串
bbbbbb
),需要 \(2n\) 次操作 - 空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目
解法三:滑动窗口(优化版)
思路
假设当窗口的右边界向右滑动添加一个字符后,窗口中的子串存在重复的字符,按照解法二的方法,则会将窗口的左边界向右滑动一个字符直到窗口中不存在重复字符为止。这样的做法其实是不高效的,比如重复的字符位于子串的右半部分,那么至少需要迭代 \(len(\text{substring}) / 2\) 次才能使得窗口包含的子串不包含重复的字符。
因此,为了更加高效地完成这个过程,我们可以借助一个 map
——保存字符最近一次的地址。当子串因为添加一个字符发生重复时,从 map
中拿出新增字符最近一次的地址(索引),并将窗口的左边界直接移动到该地址的下一个字符,则此时窗口中的子串不再包含重复的字符。
Java 实现
class Solution {
public int lengthOfLongestSubstring(String s) {
int ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int l = 0, r = 0; r < s.length(); ++r) {
char c = s.charAt(r);
if (map.containsKey(c) && l <= map.get(c)) {
l = map.get(c) + 1;
} else {
ans = Math.max(r - l + 1, ans);
}
map.put(c, r);
}
return ans;
}
}
Python 实现
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
l, max_len = 0, 0
chars_has_seen = dict()
for r, char in enumerate(s):
if char in chars_has_seen and l <= chars_has_seen[char]:
l = chars_has_seen[char] + 1
else:
max_len = max(r - l + 1, max_len)
chars_has_seen[char] = r
return max_len
复杂度分析
- 时间复杂度:\(O(n)\),其中 \(n\) 表示字符串的长度
- 空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目
解法四:滑动窗口(已知字符集)
思路
假设我们现在已经知道所有可能出现的字符,比如 ASCII
字符集,那么就可以用大小固定的数组代替 map
去存储字符的位置。
Java 实现
public class Solution {
public int lengthOfLongestSubstring(String s) {
int ans = 0;
int[] index = new int[128];
for (int l = 0, r = 0; r < s.length(); ++r) {
l = Math.max(index[s.charAt(r)], l);
ans = Math.max(r - l + 1, ans);
index[s.charAt(r)] = r + 1;
}
return ans;
}
}
Python 实现
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
all_chars = [0 for _ in range(128)]
l, max_len = 0, 0
for r, char in enumerate(s):
l = max(all_chars[ord(char)], l) # 得到窗口的左边界
max_len = max(r - l + 1, max_len) # 保留最长长度
all_chars[ord(char)] = r + 1 # 添加/更新字符的位置
return max_len
复杂度分析
- 时间复杂度:\(O(n)\),其中 \(n\) 表示字符串的长度
- 空间复杂度:\(O(\min(n, \,m))\),其中,\(n\) 表示字符串的长度,\(m\) 表示字符集中字符的数目
【LeetCode题解】3_无重复字符的最长子串(Longest-Substring-Without-Repeating-Characters)的更多相关文章
- LeetCode 3: 无重复字符的最长子串 Longest Substring Without Repeating Characters
题目: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. Given a string, find the length of the longest substring withou ...
- [Swift]LeetCode3. 无重复字符的最长子串 | Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...
- LeetCode Golang 3. 无重复字符的最长子串
3. 无重复字符的最长子串 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串 ...
- python刷LeetCode:3.无重复字符的最长子串
难度等级:中等 题目描述: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 ...
- 【LeetCode】3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc&qu ...
- LeetCode: 3 无重复字符的最长子串 (Java)
3. 无重复字符的最长子串 https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ 最初始的解 ...
- LeetCode刷题--无重复字符的最长子串(中等)
题目描述: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 " ...
- LeetCode,3. 无重复字符的最长子串
看了各位大神的,真是难堪,尤其是各种c++动不动就击败99%...我用python,换了三次算法,改了十几次bug,才击败5%....贴出来纪念下吧. 题目如下: 给定一个字符串,请你找出其中不含有重 ...
- leetcode题目3.无重复字符的最长子串(中等)
题目描述: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "a ...
随机推荐
- 关于分页插件PageHelper
上课的时候学习了分页插件,感受到了它的强大,这里总结如下: 1.首先在spring配置文件中引入依赖jar包: <dependency> <groupId>com.github ...
- cxGrid用法-最新
cxGrid用法-最新 在做AdoHelper实用程序的时候,我用了DevExpress的cxGrid控件.在此之前用的是dbgrid,考虑到不能把所有的数据都拉到本地,我用了动态生成的select ...
- ReportMachine常见问题
ReportMachine常见问题 2012-06-22 12:26:50| 分类: Delphi|举报|字号 订阅 下载LOFTER我的照片书 | 1.不打印特定的MemoVie ...
- Android x86模拟器Intel Atom x86 System Image配置与使用方法
Android x86模拟器Intel Atom x86 System Image配置与使用方法 前言: 大家现在开发使用的Android 模拟器模拟的是 ARM 的体系结构(ar ...
- Web应用安全之Response Header里的敏感信息
Web应用安全之Response Header 文/玄魂 目录 Web应用安全之Response Header 前言 1.1 那些敏感的header 1.2 删除敏感的header 1.2.1 删除 ...
- 数独 php
数独求解程序 php版 转载请注明出处:http://xiezhenye.com/2008/06/%e6%95%b0%e7%8b%ac%e6%b1%82%e8%a7%a3%e7%a8%8b%e5% ...
- SQL SERVER的锁机制(三)——概述(锁与事务隔离级别)
五.锁与事务隔离级别 事务隔离级别简单的说,就是当激活事务时,控制事务内因SQL语句产生的锁定需要保留多入,影响范围多大,以防止多人访问时,在事务内发生数据查询的错误.设置事务隔离级别将影响整条连接. ...
- Windows8.1 安装SQL Server2012——部分组件安装不成功!(提示安装.NET 3.5时出错,无Internet情况下利用win8.1安装镜像安装.NET 3.5)
虽然从事着与开发毫无关系的工作,但却也断断续续维持了近6年的WEB开发,有时因为其它工作原因,可能每做一个项目的时间间隔比较大,有时甚至在做的一个项目因为其他事情而停滞几个月之久(有些项目是自己兴趣或 ...
- 【推荐】Win7任务栏增强工具 7+ Taskbar Tweaker 强大的任务栏标签管理工具
我曾经推荐过一款XP的任务栏管理工具 Taskix,这是一款在XP系统中拖动任务栏内标签的小工具. XP 32位可以下载我汉化的版本 http://www.cnblogs.com/clso/archi ...
- 重写TreeView,多层级节点下批量显示图片,图片支持缩略图和文件名列表切换,支持调用者动态匹配选中,支持外界拖入图片并添加到对应节点下
1.先看下整体效果 2.前端代码 <UserControl x:Class="iPIS.UI.Base.Tree.ImageTreeControl" xmlns=" ...