Leetcode: Longest Substring with At Most Two Distinct Characters

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

For example, Given s = “eceba”,

T is "ece" which its length is 3.

方法一:用HashMap, map里面存元素及其出现次数。维护一个最大长度。用两个指针,右指针一直走到出现3个dinstinct character为止。然后调整左指针删元素,直接从左往右逐个字符的删除,一直删到某个字符不会再出现。判断字符被删光就看次数是否减为了0.

 

采用方法:

 1 public class Solution {
 2     public int lengthOfLongestSubstringTwoDistinct(String s) {
 3         if (s==null || s.length()==0) return 0;
 4         HashMap<Character, Integer> map = new HashMap<Character, Integer>();
 5         int longest = 0;
 6         int l = 0;
 7         int r = 0;
 8         while (r < s.length()) {
 9             char c = s.charAt(r);
10             if (map.containsKey(c)) {
11                 map.put(c, map.get(c)+1);
12             }
13             else {
14                 map.put(c, 1);
15                 if (map.size() >= 3) {
16                     longest = Math.max(longest, r-l);
18 while (map.size() >= 3 && l <= r) {
char d = s.charAt(l);
19 if (map.get(d) > 1) map.put(d, map.get(d)-1); 20 else map.remove(d); 21 l++; 22 } 23 } 24 } 25 r++; 26 } 27 longest = Math.max(longest, r-l); 28 return longest; 29 } 30 }

这里每个元素都会进入窗口一次,最多出窗口一次,所以平摊在每个元素上的时间复杂度是O(1),总时间复杂度为O(N)

网上另一种做法,可以删的更快。

转:

这题的线性解法是维护一个sliding window,里面的子字符串只含最多两个不同字符。当要添加一个新字符时,需要完全去掉之前的某一个字符的所有出现。这里有两个问题需要考虑:

1)在已有的两个字符中,如何选择该去掉其所有出现的字符?

2)在选定该去掉的字符后,如何改调整窗口?

对于问题1,窗口起始处的字符是一定会被去掉的,但是否总是应该选择这个字符然后去掉其所有出现么?以“abac”为例,可以发现当扫描到c时,a是一定会被去掉的,但是如果去掉所有出现过的a,那么最后只剩下"c"了。这时应该是去掉所有出现的b,顺便去掉了最开始的a,从而得到"ac"。由此观之,选择标准应该是字符的最后出现的位置,最后出现的位置越左(早),则其出现被全部删除后所减小的长度越少。因此,应该删光最后出现位置在最左的字符。

对于问题2,由于题目规定了窗口里最多只会有2种字符,其实怎么删都可以:慢点的可以从左到右逐个删除,快点的可以直接让新窗口以所选字符的最后出现位置的下一个字符打头。

以下代码用了一个Map结构来表示sliding window,key为字符,value为对应的最后出现位置。其实也可以完全避免使用Map,因为里面的entry最多只有两个,比较浪费空间。这里是考虑到了后来的扩展以及代码的维护。

这里为了获得Map的最后位置最早出现的字符,遍历了所有的entry。这其实是非常低效的,但考虑到Map里实际只有2个元素,所以遍历的开销也很小,可以忽略不计。

 1     public int lengthOfLongestSubstringTwoDistinct(String s) {
 2         int start = 0;
 3         int maxLen = 0;
 4 
 5         // Key: letter; value: the index of the last occurrence.
 6         Map<Character, Integer> map = new HashMap<Character, Integer>();
 7         int i;
 8         for (i = 0; i < s.length(); ++i) {
 9             char c = s.charAt(i);
10             if (map.size() == 2 && !map.containsKey(c)) {
11                 // Pick the character with the leftmost last occurrence.
12                 char charEndsMostLeft = ' ';
13                 int minLast = s.length();
14                 for (char ch : map.keySet()) {
15                     int last = map.get(ch);
16                     if (last < minLast) {
17                         minLast = last;
18                         charEndsMostLeft = ch;
19                     }
20                 }
21 
22                 map.remove(charEndsMostLeft);
23                 start = minLast + 1;
24             }
25             map.put(c, i);
26             maxLen = Math.max(maxLen, i - start + 1);
27         }
28         return maxLen;
29     }

 

转载于:https://www.cnblogs.com/EdwardLiu/p/4241421.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值