159. 最多包含两个不同字符的最长子串
一、原题描述与小白翻译
原题:Given a string s, find the length of the longest substring t that contains at most 2 distinct characters.
给定一个字符串 s ,找到最多包含 2 个不同字符的最长子字符串 t 的长度。
二、例子
三、 小白做题
坐在自习室正在准备刷题的小白看到这道题,想想自己那可是没少和白月光做题呢,也不知道小美刷题刷到哪里了,这题怎么还没来问我,难道是因为没买会员看不到这道题?
这时候黑长直女神突然进到教室过来问:小白,你看到二叉树题目了吗,这道159的题目,感觉描述的比较直接,类型算是String类的题目,你有什么好思路吗?
小白内心镇定:这机会不就来了吗,小美,你喜欢李庚希吗,我请你去看《我们一起摇太阳》啊?
哦,不是,题目描述意思说的简单一些。
这题其实意思上比较好理解,对于示例一来说,一共五个字符,其中要找出一个字符段中只包含两个不同的字符,并且是最长的子字符串是3。
这就是问题所要求的,当涉及子字符串子数组时候,我们又能利用到滑动窗口技术。因此,我们滑动窗口向右移动,直到值是有效的,当窗口向左收缩,来找到我们的窗口,然后右侧运行直到树的长度。
这里我们可以创建一个Map<character, Interger>来创建一个记录每个字符频率的map。
小美:小伙子,可以啊,这不仅进行了解题,阅读理解也有俩下子!不过电影票要你买单哦。
小白:没问题,谁叫为了“真爱”呢。
四、真正面试环节
面试官:你可以解答这道”从二叉树构造字符串“的题目吗,来看看你对树结构的理解。
小白:嘿嘿,这不巧了么这不是。
Java 版本
public static int lengthOfLongestSubstringTwoDistinct(String s) {
// 空字符串或者null直接返回0
if (s == null || s.length() == 0) {
return 0;
}
int n = s.length();
// 哈希表记录每个字符出现的次数
HashMap<Character, Integer> map = new HashMap<>();
// 不同字符类型的数量
int distinctCount = 0, result = 0;
// 遍历字符串中每一个字符, i代表右边界,循环一次带表右移
for (int i = 0, j = 0; i < n; i++) {
char ci = s.charAt(i);
// 用getOrDefault更新字符出现的次数,如果是新加入的,那么给定默认值0,开始计数
map.put(ci, map.getOrDefault(ci, 0) + 1);
// 如果当前字符出现次数为1,则不同字符类型数量加1
if (map.get(ci) == 1) {
distinctCount++;
}
// 如果记录新生成字符的数字已经超过要求的2,需要缩小滑动窗口
while (distinctCount > 2) {
char leftChar = s.charAt(j);
// 更新左边界字符出现的次数
map.put(leftChar, map.get(leftChar) - 1);
// 当这个cj字符值是0的情况下,那么不同字符的总数减去1.
if (map.get(leftChar) == 0) {
distinctCount--;
}
// 左边界右移
j++;
}
// 更新这个新结果的字符串长度
result = Math.max(result, i - j + 1);
}
return result;
}
小明:OK,完事儿,等着面试官来表扬自己吧。他肯定会说:小子,你是个好手!工位都给你准备好了,工资你说了算。
面试官:矮油,不错啊,不过你这能不能写python和c++版本的啊。
小明OS:今年这个找工市场,人言洛阳花似锦,偏我来时不逢春。。。不是,怎么还让我写这么多语法啊!
面试官:这不是咱们面对人群广泛吗,不会写可有很多俊男靓女会写哦。
Python 版本
def length_of_longest_substring_two_distinct(s):
"""
求解字符串中最多包含两个不同字符的最长子串
Args:
s: 字符串
Returns:
最长子串长度
"""
# 空字符串或者None直接返回0
if not s:
return 0
n = len(s)
# 哈希表记录每个字符出现的次数
char_count = {}
# 不同字符类型的数量
distinct_count = 0
# 最长子串长度
result = 0
# 左右指针
i, j = 0, 0
# 遍历字符串
for i in range(n):
c = s[i]
# 更新字符出现的次数
char_count[c] = char_count.get(c, 0) + 1
# 如果当前字符出现次数为1,则不同字符类型数量加1
if char_count[c] == 1:
distinct_count += 1
# 当不同字符类型数量超过2时,需要缩小滑动窗口
while distinct_count > 2:
left_char = s[j]
# 更新左边界字符出现的次数
char_count[left_char] -= 1
# 如果左边界字符出现次数为0,则不同字符类型数量减1
if char_count[left_char] == 0:
distinct_count -= 1
# 左边界右移
j += 1
# 更新最长子串长度
result = max(result, i - j + 1)
return result
C++ 版本
#include <iostream>
#include <unordered_map>
using namespace std;
int lengthOfLongestSubstringTwoDistinct(string s) {
// 空字符串或者""直接返回0
if (s.empty()) {
return 0;
}
int n = s.length();
// 哈希表记录每个字符出现的次数
unordered_map<char, int> char_count;
// 不同字符类型的数量
int distinct_count = 0;
// 最长子串长度
int result = 0;
// 左右指针
int i = 0, j = 0;
// 遍历字符串
for (i = 0; i < n; i++) {
char c = s[i];
// 更新字符出现的次数
char_count[c]++;
// 如果当前字符出现次数为1,则不同字符类型数量加1
if (char_count[c] == 1) {
distinct_count++;
}
// 当不同字符类型数量超过2时,需要缩小滑动窗口
while (distinct_count > 2) {
char left_char = s[j];
// 更新左边界字符出现的次数
char_count[left_char]--;
// 如果左边界字符出现次数为0,则不同字符类型数量减1
if (char_count[left_char] == 0) {
distinct_count--;
}
// 左边界右移
j++;
}
// 更新最长子串长度
result = max(result, i - j + 1);
}
return result;
}
小白:您好,面试官,这回可以了吧,我终于有钱请小美看电影了!
复杂度分析
- 时间复杂度O(n) :我们遍历包含 n 元素的字符串一次。由于哈希表将查找时间减少到 O(1),因此总体时间复杂度为 O(n) 。
- 空间复杂度O(n):所需的额外空间取决于哈希表中存储的项目数量,该哈希表恰好存储 n 元素。
🍀🍀🍀 更多精彩免费分析题目可在这里观看 面试数据结构与算法总结分类+leetcode目录【基础版】🍀🍀🍀