Given a string s , find the length of the longest substring t  that contains at most 2 distinct characters.

Example 1:

Input: "eceba"
Output: 3
Explanation: tis "ece" which its length is 3.

Example 2:

Input: "ccaabbb"
Output: 5
Explanation: tis "aabbb" which its length is 5.

这道题给我们一个字符串,让求最多有两个不同字符的最长子串。那么首先想到的是用 HashMap 来做,HashMap 记录每个字符的出现次数,然后如果 HashMap 中的映射数量超过两个的时候,这里需要删掉一个映射,比如此时 HashMap 中e有2个,c有1个,此时把b也存入了 HashMap,那么就有三对映射了,这时 left 是0,先从e开始,映射值减1,此时e还有1个,不删除,left 自增1。这时 HashMap 里还有三对映射,此时 left 是1,那么到c了,映射值减1,此时e映射为0,将e从 HashMap 中删除,left 自增1,然后更新结果为 i - left + 1,以此类推直至遍历完整个字符串,参见代码如下:

解法一:

class Solution {
public:
int lengthOfLongestSubstringTwoDistinct(string s) {
int res = , left = ;
unordered_map<char, int> m;
for (int i = ; i < s.size(); ++i) {
++m[s[i]];
while (m.size() > ) {
if (--m[s[left]] == ) m.erase(s[left]);
++left;
}
res = max(res, i - left + );
}
return res;
}
};

我们除了用 HashMap 来映射字符出现的个数,还可以映射每个字符最新的坐标,比如题目中的例子 "eceba",遇到第一个e,映射其坐标0,遇到c,映射其坐标1,遇到第二个e时,映射其坐标2,当遇到b时,映射其坐标3,每次都判断当前 HashMap 中的映射数,如果大于2的时候,那么需要删掉一个映射,还是从 left=0 时开始向右找,看每个字符在 HashMap 中的映射值是否等于当前坐标 left,比如第一个e,HashMap 此时映射值为2,不等于 left 的0,那么 left 自增1,遇到c的时候,HashMap 中c的映射值是1,和此时的 left 相同,那么我们把c删掉,left 自增1,再更新结果,以此类推直至遍历完整个字符串,参见代码如下:

解法二:

class Solution {
public:
int lengthOfLongestSubstringTwoDistinct(string s) {
int res = , left = ;
unordered_map<char, int> m;
for (int i = ; i < s.size(); ++i) {
m[s[i]] = i;
while (m.size() > ) {
if (m[s[left]] == left) m.erase(s[left]);
++left;
}
res = max(res, i - left + );
}
return res;
}
};

后来又在网上看到了一种解法,这种解法是维护一个 sliding window,指针 left 指向起始位置,right 指向 window 的最后一个位置,用于定位 left 的下一个跳转位置,思路如下:

1. 若当前字符和前一个字符相同,继续循环。

2. 若不同,看当前字符和 right 指的字符是否相同

(1) 若相同,left 不变,右边跳到 i - 1

(2) 若不同,更新结果,left 变为 right+1,right 变为 i - 1

最后需要注意在循环结束后,还要比较结果 res 和 s.size() - left 的大小,返回大的,这是由于如果字符串是 "ecebaaa",那么当 left=3 时,i=5,6 的时候,都是继续循环,当i加到7时,跳出了循环,而此时正确答案应为 "baaa" 这4个字符,而我们的结果 res 只更新到了 "ece" 这3个字符,所以最后要判断 s.size() - left 和结果 res 的大小。

另外需要说明的是这种解法仅适用于于不同字符数为2个的情况,如果为k个的话,还是需要用上面两种解法。

解法三:

class Solution {
public:
int lengthOfLongestSubstringTwoDistinct(string s) {
int left = , right = -, res = ;
for (int i = ; i < s.size(); ++i) {
if (s[i] == s[i - ]) continue;
if (right >= && s[right] != s[i]) {
res = max(res, i - left);
left = right + ;
}
right = i - ;
}
return max(s.size() - left, res);
}
};

还有一种不使用 HashMap 的解法,是在做 Fruit Into Baskets 这道题的时候在论坛上看到的,其实这两道题除了背景设定之外没有任何的区别,代码基本上都可以拷来直接用的。这里使用若干的变量,其中 cur 为当前最长子串的长度,first 和 second 为当前候选子串中的两个不同的字符,cntLast 为 second 字符的连续长度。遍历所有字符,假如遇到的字符是 first 和 second 中的任意一个,那么 cur 可以自增1,否则 cntLast 自增1,因为若是新字符的话,默认已经将 first 字符淘汰了,此时候选字符串由 second 字符和这个新字符构成,所以当前长度是 cntLast+1。然后再来更新 cntLast,假如当前字符等于 second 的话,cntLast 自增1,否则均重置为1,因为 cntLast 统计的就是 second 字符的连续长度。然后再来判断若当前字符不等于 second,则此时 first 赋值为 second, second 赋值为新字符。最后不要忘了用 cur 来更新结果 res,参见代码如下:

解法四:

class Solution {
public:
int lengthOfLongestSubstringTwoDistinct(string s) {
int res = , cur = , cntLast = ;
char first, second;
for (char c : s) {
cur = (c == first || c == second) ? cur + : cntLast + ;
cntLast = (c == second) ? cntLast + : ;
if (c != second) {
first = second; second = c;
}
res = max(res, cur);
}
return res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/159

类似题目:

Fruit Into Baskets

Longest Substring Without Repeating Characters

Sliding Window Maximum

Longest Substring with At Most K Distinct Characters

Subarrays with K Different Integers

参考资料:

https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/

https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/discuss/49759/Share-my-c%2B%2B-solution

https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/discuss/49687/Clean-11-lines-AC-answer-O(1)-space-O(n)-time.

https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/discuss/49682/Simple-O(n)-java-solution-easily-extend-to-k-characters

https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/discuss/49708/Sliding-Window-algorithm-template-to-solve-all-the-Leetcode-substring-search-problem.

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 159. Longest Substring with At Most Two Distinct Characters 最多有两个不同字符的最长子串的更多相关文章

  1. [LeetCode] 340. Longest Substring with At Most K Distinct Characters 最多有K个不同字符的最长子串

    Given a string, find the length of the longest substring T that contains at most k distinct characte ...

  2. [LeetCode] Longest Substring with At Most Two Distinct Characters 最多有两个不同字符的最长子串

    Given a string S, find the length of the longest substring T that contains at most two distinct char ...

  3. [LeetCode] Longest Substring with At Most K Distinct Characters 最多有K个不同字符的最长子串

    Given a string, find the length of the longest substring T that contains at most k distinct characte ...

  4. [leetcode]159. Longest Substring with At Most Two Distinct Characters至多包含两种字符的最长子串

    Given a string s , find the length of the longest substring t  that contains at most 2 distinct char ...

  5. ✡ leetcode 159. Longest Substring with At Most Two Distinct Characters 求两个字母组成的最大子串长度 --------- java

    Given a string, find the length of the longest substring T that contains at most 2 distinct characte ...

  6. 395 Longest Substring with At Least K Repeating Characters 至少有K个重复字符的最长子串

    找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k .输出 T 的长度.示例 1:输入:s = "aaabb", k = 3输出:3最 ...

  7. leetcode[159] Longest Substring with At Most Two Distinct Characters

    找到最多含有两个不同字符的子串的最长长度.例如:eoeabc,最长的是eoe为3,其他都为2. 思路: 用p1,p2表示两种字符串的最后一个出现的下标位置.初始p1为0. p2为-1.start初始化 ...

  8. [leetcode]340. Longest Substring with At Most K Distinct Characters至多包含K种字符的最长子串

    Given a string, find the length of the longest substring T that contains at most k distinct characte ...

  9. 【LeetCode】159. Longest Substring with At Most Two Distinct Characters

    Difficulty: Hard  More:[目录]LeetCode Java实现 Description Given a string S, find the length of the long ...

随机推荐

  1. golang编译器:gccgo vs gc

    GCC是一个功能强大的编译器,不仅可以编译我们很熟悉的C/C++,也可以做为Fortran.Pascal.Objective-C等语言的编译器.而GCCGO则是GCC专门用来编译Golang语言的.G ...

  2. 【Python】itertools之product函数

    [转载]源博客 product 用于求多个可迭代对象的笛卡尔积(Cartesian Product),它跟嵌套的 for 循环等价.即: product(A, B) 和 ((x,y) for x in ...

  3. OWIN,Katana,identity整体概述

    在用asp.net identity的时候,发现很多概念不是很懂,特地去查资料了解了一些相关信息,现在做下笔记. 1.OWIN,OWIN是Open Web Server Interface for . ...

  4. SAP MM MIGO过账报错 - 用本币计算的余额 - 之对策

    SAP MM MIGO过账报错 - 用本币计算的余额 - 之对策 使用MIGO事务代码对采购订单4500000191,执行收货,系统报错: 详细错误信息如下: 用本币计算的余额 消息号 F5703 诊 ...

  5. android studio学习----gradle基础

    Gradle是一种依赖管理工具,基于Groovy语言,面向Java应用为主,它抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的内部领域特定(DSL)语言. 安装Gradle 在And ...

  6. 【转】Git使用教程之远程仓库

    1.远程仓库 在了解之前,先注册github账号,由于你的本地Git仓库和github仓库之间的传输是通过SSH加密的,所以需要一点设置: 第一步:创建SSH Key.在用户主目录下,看看有没有.ss ...

  7. jdbc、Mybatis、Hibernate介绍(非原创)

    文章大纲 一.jdbc介绍二.Mybatis介绍三.Hibernate介绍四.jdbc.Mybatis.Hibernate比较五.参考文章   一.jdbc介绍 1. jdbc编程步骤 (1)加载数据 ...

  8. 在VideoFileClip函数中获取“OSError:[WinError 6]句柄无效”

    我正在使用python通过导入moviepy库创建一个程序,但收到以下错误: from moviepy.editor import VideoFileClip white_output = 'vide ...

  9. java引用的强制转型

    在java的面向对象的特性里,父类的引用可以指向子类的实例对象.但是,如果一个引用b(b本身指向了一个对象)想赋值给引用a,b不是a的类型且不是a的子类类型,那么就需要强制转换,并有失败的可能性,这个 ...

  10. firewalld规则写法

    需要注意的是Firewalld中的区域与接口 一个网卡仅能绑定一个区域.比如: eth0-->A区域 但一个区域可以绑定多个网卡.比如: B区域-->eth0.eth1.eth2 可以根据 ...