双指针 子字符串问题

  LeetCode 76. Minimum Window Substring

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

For example,
S = “ADOBECODEBANC”
T = “ABC”
Minimum window is “BANC”.

Note:
If there is no such window in S that covers all characters in T, return the empty string “”.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

  其实仔细思考一下感觉应该是可以思考出方法的才对。至少一上来就能感觉到是双指针的题。
  一般来说,给定一个字符串或序列,要求低复杂度找出其中某个子序列,往往可以考虑双指针方法

  双指针方案注意点:
  1. 第一个指针就是简单遍历 for(int i = 0; i<arr.length; i++)。关键是第二个指针,第二个指针可能是for(int j = 0; j<i; j++) 这种;也可能是满足某些条件时,执行j++j-- 等,。具体方案视题意千变万化。
  2. 双指针的目标可能是区间[left, right],而这个区间可能通过这几种途径获得:可能是区间[j,i] ,也可能是区间[0,j]和[j+1,i], 再或者是一次次遍历中某个计数值count的逐渐达到或又远离target
  3. 有时候当满足非最优的题设目标时,可以获得一个区间[j,i],并记录下此时的左右边界leftright 或者其他计算结果。而为了获得更优的题设目标,将改变 ji ,继续遍历。

本题代码如下:

public String minWindow(String s, String t) {
    if(s == null || s.length() < t.length() || s.length() == 0){
        return "";
    }
    HashMap<Character,Integer> map = new HashMap<Character,Integer>();
    for(char c : t.toCharArray()){
        if(map.containsKey(c)){
            map.put(c,map.get(c)+1);
        }else{
            map.put(c,1);
        }
    }
    int left = 0;
    int minLeft = 0;
    int minLen = s.length()+1;
    int count = 0;
    for(int right = 0; right < s.length(); right++){
        if(map.containsKey(s.charAt(right))){
            //如果map里有s当前在right处可以提供给t的字符
            //就将s的字符提供给t,t在该字符处的需求-1
            map.put(s.charAt(right),map.get(s.charAt(right))-1);

            //每解决t的一个字符需求,将count计数+1,count==t.length()时表示找到一个符合要求的区间
            if(map.get(s.charAt(right)) >= 0){
                count ++;
            }
            //双指针的目标可能一次次遍历中某个计数值count的逐渐达到或又远离target
            //每解决完一次t的完整字符需求,就更新一次区间左指针和区间长度
            while(count == t.length()){
                if(right-left+1 < minLen){
                    minLeft = left;
                    minLen = right-left+1;
                }            
                //将本轮找到的区间中最左边的一个t所需字符剔除,以便左右指针可以继续移动
                if(map.containsKey(s.charAt(left))){
                    map.put(s.charAt(left),map.get(s.charAt(left))+1);
                    if(map.get(s.charAt(left)) > 0){
                        count --;
                    }
                }
                //满足某些条件时,执行 j++ 或 j-- 
                left ++ ;
            }
        }
    }
    if(minLen>s.length())  
        return "";

    return s.substring(minLeft,minLeft+minLen);        
}
  引用附上使用双指针和Map解决寻找子字符串问题的解决模板

  非常厉害!

The code of solving this problem is below. It might be the shortest among all solutions provided in Discuss.

string minWindow(string s, string t) {
        vector<int> map(128,0);
        for(auto c: t) map[c]++;
        int counter=t.size(), begin=0, end=0, d=INT_MAX, head=0;
        while(end<s.size()){
            if(map[s[end++]]-->0) counter--; //in t
            while(counter==0){ //valid
                if(end-begin<d)  d=end-(head=begin);
                if(map[s[begin++]]++==0) counter++;  //make it invalid
            }  
        }
        return d==INT_MAX? "":s.substr(head, d);
    }

Here comes the template.

For most substring problem, we are given a string and need to find a substring of it which satisfy some restrictions. A general way is to use a hashmap assisted with two pointers. The template is given below.

int findSubstring(string s){
        vector<int> map(128,0);
        int counter; // check whether the substring is valid
        int begin=0, end=0; //two pointers, one point to tail and one  head
        int d; //the length of substring

        for() { /* initialize the hash map here */ }

        while(end<s.size()){

            if(map[s[end++]]-- ?){  /* modify counter here */ }

            while(/* counter condition */){ 

                 /* update d here if finding minimum*/

                //increase begin to make it invalid/valid again

                if(map[s[begin++]]++ ?){ /*modify counter here*/ }
            }  

            /* update d here if finding maximum*/
        }
        return d;
  }

One thing needs to be mentioned is that when asked to find maximum substring, we should update maximum after the inner while loop to guarantee that the substring is valid. On the other hand, when asked to find minimum substring, we should update minimum inside the inner while loop.

The code of solving Longest Substring with At Most Two Distinct Characters is below:

int lengthOfLongestSubstringTwoDistinct(string s) {
        vector<int> map(128, 0);
        int counter=0, begin=0, end=0, d=0; 
        while(end<s.size()){
            if(map[s[end++]]++==0) counter++;
            while(counter>2) if(map[s[begin++]]--==1) counter--;
            d=max(d, end-begin);
        }
        return d;
    }

The code of solving Longest Substring Without Repeating Characters is below:

Update 01.04.2016, thanks @weiyi3 for advise.

int lengthOfLongestSubstring(string s) {
        vector<int> map(128,0);
        int counter=0, begin=0, end=0, d=0; 
        while(end<s.size()){
            if(map[s[end++]]++>0) counter++; 
            while(counter>0) if(map[s[begin++]]-->1) counter--;
            d=max(d, end-begin); //while valid, update d
        }
        return d;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值