算法题法算day8
lief
[up主专用,视频内嵌代码贴在这]
无重复字符的最长子串
问题描述
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。注意 “bca” 和 “cab” 也是正确答案。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,”pwke” 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
算法分析
这个问题使用滑动窗口(双指针)的方法来解决。我们维护一个窗口,窗口内的字符都是不重复的。通过左右两个指针来定义窗口的边界:
右指针不断向右移动,扩展窗口
当遇到重复字符时,左指针向右移动,缩小窗口
在每次扩展窗口后,记录当前窗口的长度,并更新最大长度
这种方法的时间复杂度是O(n),其中n是字符串的长度,因为每个字符最多被访问两次(一次由右指针,一次由左指针)。空间复杂度是O(min(m, n)),其中m是字符集的大小。
可视化说明(以“abcabcbb”为例)
1 2 3 4 5 6 7 8 9 10
| 初始: [a]bcabcbb 窗口: "a" 长度:1 扩展: [ab]cabcbb 窗口: "ab" 长度:2 扩展: [abc]abcbb 窗口: "abc" 长度:3 遇到重复: a[bca]bcbb 窗口: "bca" 长度:3 遇到重复: ab[cab]cbb 窗口: "cab" 长度:3 遇到重复: abc[abc]bb 窗口: "abc" 长度:3 遇到重复: abca[bc]bb 窗口: "bc" 长度:2 遇到重复: abcab[cb]b 窗口: "cb" 长度:2 遇到重复: abcabc[b]b 窗口: "b" 长度:1 遇到重复: abcabcb[b] 窗口: "b" 长度:1
|
python版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| def lengthOfLongestSubstring(s): """ 找出不包含重复字符的最长子串的长度 使用滑动窗口方法 """ # 创建一个集合来存储窗口中的字符,用于快速判断字符是否重复 char_set = set() # 左指针,表示当前窗口的起始位置 left = 0 # 记录最长子串的长度 max_length = 0 # 右指针遍历整个字符串 for right in range(len(s)): # 如果当前字符已经在窗口中存在 while s[right] in char_set: # 从窗口中移除左指针指向的字符 char_set.remove(s[left]) # 左指针向右移动,缩小窗口 left += 1 # 将当前字符添加到窗口中 char_set.add(s[right]) # 更新最大长度:当前窗口的长度是 right - left + 1 max_length = max(max_length, right - left + 1) return max_length
# 测试代码 if __name__ == "__main__": # 示例1 s1 = "abcabcbb" print(lengthOfLongestSubstring(s1)) # 输出: 3 # 示例2 s2 = "bbbbb" print(lengthOfLongestSubstring(s2)) # 输出: 1 # 示例3 s3 = "pwwkew" print(lengthOfLongestSubstring(s3)) # 输出: 3
|
✅ 说明:
- 时间复杂度:O(n),其中n是字符串的长度
- 空间复杂度:O(min(m, n)),其中m是字符集的大小
java版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| import java.util.HashMap; import java.util.Map;
public class LongestSubstringWithoutRepeating { public int lengthOfLongestSubstring(String s) { // 如果字符串为空或长度为0,直接返回0 if (s == null || s.length() == 0) { return 0; } // 使用HashMap来存储字符和它们最近出现的位置 Map<Character, Integer> charIndexMap = new HashMap<>(); //new HashMap<>(): 创建HashMap类的实例 //Map<Character, Integer>: 声明一个映射接口,键是Character类型,值是Integer类型;charIndexMap: 变量名 // 初始化最大长度为0 int maxLength = 0; // 初始化滑动窗口的左边界 int left = 0; // 遍历字符串,右指针从0到字符串末尾 for (int right = 0; right < s.length(); right++) { char currentChar = s.charAt(right); //字符串类的方法,用于获取指定位置的字符 // 如果当前字符已经在窗口中出现过 if (charIndexMap.containsKey(currentChar)) { // 更新左边界,确保窗口内没有重复字符 // 取最大值是为了防止左边界向左移动 left = Math.max(left, charIndexMap.get(currentChar) + 1); } // 更新当前字符的最新位置 charIndexMap.put(currentChar, right); // 计算当前窗口的长度,并更新最大长度 maxLength = Math.max(maxLength, right - left + 1); } return maxLength; } // 测试代码 public static void main(String[] args) { LongestSubstringWithoutRepeating solution = new LongestSubstringWithoutRepeating(); // 示例1 String s1 = "abcabcbb"; System.out.println("输入: " + s1); System.out.println("输出: " + solution.lengthOfLongestSubstring(s1)); // 输出: 3 // 示例2 String s2 = "bbbbb"; System.out.println("输入: " + s2); System.out.println("输出: " + solution.lengthOfLongestSubstring(s2)); // 输出: 1 // 示例3 String s3 = "pwwkew"; System.out.println("输入: " + s3); System.out.println("输出: " + solution.lengthOfLongestSubstring(s3)); // 输出: 3 // 额外测试用例 String s4 = "abcde"; System.out.println("输入: " + s4); System.out.println("输出: " + solution.lengthOfLongestSubstring(s4)); // 输出: 5 String s5 = "aab"; System.out.println("输入: " + s5); System.out.println("输出: " + solution.lengthOfLongestSubstring(s5)); // 输出: 2 } }
|
执行过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 初始状态: left=0, maxLength=0, charIndexMap={}
第1步: right=0, char='a' charIndexMap中没有'a',直接添加 charIndexMap={'a':0} 当前窗口长度=1,maxLength=1
第2步: right=1, char='b' charIndexMap中没有'b',直接添加 charIndexMap={'a':0, 'b':1} 当前窗口长度=2,maxLength=2
第3步: right=2, char='c' charIndexMap中没有'c',直接添加 charIndexMap={'a':0, 'b':1, 'c':2} 当前窗口长度=3,maxLength=3
第4步: right=3, char='a' charIndexMap中有'a',位置是0 更新left = max(0, 0+1) = 1 更新charIndexMap中'a'的位置为3 charIndexMap={'a':3, 'b':1, 'c':2} 当前窗口长度=3,maxLength=3
第5步: right=4, char='b' charIndexMap中有'b',位置是1 更新left = max(1, 1+1) = 2 更新charIndexMap中'b'的位置为4 charIndexMap={'a':3, 'b':4, 'c':2} 当前窗口长度=3,maxLength=3
第6步: right=5, char='c' charIndexMap中有'c',位置是2 更新left = max(2, 2+1) = 3 更新charIndexMap中'c'的位置为5 charIndexMap={'a':3, 'b':4, 'c':5} 当前窗口长度=3,maxLength=3
第7步: right=6, char='b' charIndexMap中有'b',位置是4 更新left = max(3, 4+1) = 5 更新charIndexMap中'b'的位置为6 charIndexMap={'a':3, 'b':6, 'c':5} 当前窗口长度=2,maxLength=3
第8步: right=7, char='b' charIndexMap中有'b',位置是6 更新left = max(5, 6+1) = 7 更新charIndexMap中'b'的位置为7 charIndexMap={'a':3, 'b':7, 'c':5} 当前窗口长度=1,maxLength=3
最终结果: 3
|