Leetcode53.最大子串和(简单)76.最小子串覆盖(困难)3. 最长子串不重复问题(中等)

Posted Dream_it_possible!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode53.最大子串和(简单)76.最小子串覆盖(困难)3. 最长子串不重复问题(中等)相关的知识,希望对你有一定的参考价值。

目录

最大子串和问题(简单)

题目

思路一 

思路二

最小子串覆盖问题(困难)

题目

思路一 

思路二

最长子串不重复问题(中等)

题目

思路一

思路二


最大子串和问题(简单)

题目

给定一个整数数组,求出数组中连续的最大的子串和。

例如:  2, -1, 3, 5, 6, -3

最大子串和为:  2-1+3+5+6=15

思路一 

        采用暴力循环的方式,找出每趟最大的子串和,然后再比较每趟的值,最大的那个值即为最大子串和。

 完整代码:

   private static Integer findMaxSumStrOne(int[] arr) 
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) 
            int temp = arr[i];
            int iMax = temp;
            for (int j = i + 1; j < arr.length; j++) 
                int current = temp + arr[j];
                if (current > iMax) 
                    iMax = current;
                
                temp = current;
            
            if (iMax > max) 
                max = iMax;
            
        
        return max;
    

       此方法虽然能求解,但是时间复杂度为0(n2), 效率比较低。

思路二

        采用淘汰的思想,每次只加对当前贡献大的数。

class Solution 

   public Integer maxSubArray(int[] arr) 
       int ans=arr[0];
       int pre=0;
       for(int i=0;i<arr.length;i++)
           pre=Math.max(arr[i],pre+arr[i]);
           ans=Math.max(ans,pre);
       
       return ans;
    



最小子串覆盖问题(困难)

题目

给定一个字符串S,字符串T, 求字符串S包含字符串T的最短子串。

例如:  S="ADOBECODEBANC", T="ABC"

输出结果: "BANC"

思路一 

        使用暴力破解的方法

        采用顺序字符串的格式获取源字符串的子串,然后用源字符串的子串对目标字符串进行逐个匹配,命中一个字符将目标字符串把该字符给剔除掉,直到目标字符串里的字符全部剔除完毕,就表示找到合适的字符串。

        每次找到符合要求的子串(Target)时,比较foundStr和符合要求的Target串的长度,如果长度比源串长度小,就把目标串赋值给foundStr,最终返回结果即可。

        也可以根据下标的差值来比较,取差值最小的那个即可。

   private static String findStr(String s, String t) 
        if (s.length() < t.length()) 
            return "";
        

        if (t.length() == 1 && s.contains(t)) 
            return t;
        

        String foundStr = "";
        for (int i = 0; i < s.length(); i++) 
            String source = s.charAt(i) + "";
            String target = t;
            int index = t.indexOf(source);
            if (index != -1) 
                target = target.substring(0, index) + target.substring(index + 1);
                // 目标的子串包含了一个才进行匹配
                for (int j = i + 1; j < s.length(); j++) 
                    String sub = s.charAt(j) + "";
                    // subStr字符串中的所有字符必须全部命中
                    int subIndex = target.indexOf(sub);
                    if (subIndex != -1) 
                        target = target.substring(0, subIndex) + target.substring(subIndex + 1);
                        if (target.length() == 0) 
                            System.out.println("符合要求:(" + i + "," + j + ")");
                            String str = s.substring(i, j + 1);
                            if ("".equals(foundStr)) 
                                foundStr = str;
                             else 
                                foundStr = str.length() <= foundStr.length() ? str : foundStr;
                            
                        
                    
                
            
        

        return foundStr;
    

打印结果:
输入: S= "CQDBANC", T="ABC"

输出: BANC

        此方法虽然可行,但是时间复杂度是o(n2), 效率比较低,在leetcode里执行会出现超时的情况。

思路二

        方法二,采用滑动窗口思想

        1) 先将子串的所有字符放到hashMap里,key为字符,value为字符的个数。

        2) 然后在主串里设置2个游标,都从左边开始移动,先移动右指针,找到符合条件的子串后,再去移动左指针。

        3) j每次找到符合子串的字符(采用map的containsKey()方法来判断),找到符合的count 加1,count初始值为0, 然后hashmap里key对应的value值也-1。同样的i也会向右移动,如果i找到符合的字符在hashmap里,并且hasmap.get(key)==0的情况下,count需要减1,因为map的key对应的value值为0表示j已经找到了符合要求的同样字符,因此count-1。

        循环直到count值等于子串的长度值,即找到了覆盖目标子串的字符串。每找到一次与len做比较,取最终长度最小的字符串即可。

        滑动窗口思想如下:

        此处需要注意的地方: 

        1)  对于j来讲, 先j++ 然后再判断 , 因为后面的字符我们是不知道的。

        2)  对于i来讲,先判断 i对应的字符是否满足条件,如果发现i 正好命中到了字符串subStr的一个字符,并且j 命中过该字符全部的次数(开始时subStr的字符个数),那么需要count--,然后再i++。

class Solution 
    public String minWindow(String s, String t) 
        // 用hashmap存放子串字符出现的个数
        HashMap<Character, Integer> hashMap = new HashMap<>();
        for (int i = 0; i < t.length(); i++) 
            if (hashMap.containsKey(t.charAt(i))) 
                hashMap.put(t.charAt(i), hashMap.get(t.charAt(i)) + 1);
             else 
                hashMap.put(t.charAt(i), 1);
            
        
        int left = 0;
        int right = -1;
        //[left,right] s中包含t字符的出现的次数
        int count = 0;
        String result = "";
        int len = s.length() + 1;
        while (left < s.length()) 
            if (right + 1 < s.length() && count < t.length()) 
                // 滑动右边窗口
                right++;
                Character rightChar=s.charAt(right);
                if (hashMap.containsKey(rightChar)) 
                    if (hashMap.get(rightChar) > 0) 
                        count++;
                    
                    // 如果正好包含右边的字符,那么就将字符出现的次数-1
                    hashMap.put(rightChar, hashMap.get(rightChar) - 1);
                
             else 
                // count 表示出现t中字符的次数,如果刚好为2,记录满足的结果后,那么就需要移动左指针继续寻找符合条件的最小子串。
                // 否则滑动左边窗口
                Character leftChar=s.charAt(left);
                if (hashMap.containsKey(leftChar)) 
                    // 当map中的次数为0时,t中的字符出现的次数-1。
                    if (hashMap.get(leftChar) == 0) 
                        count--;
                    
                    // 每滑动左边窗口,map中的出现的字符+1
                    hashMap.put(leftChar, hashMap.get(leftChar) + 1);
                
                left++;
            
            if (count == t.length()) 
                if (right - left + 1 < len) 
                    len = right - left + 1;
                    result = s.substring(left, right + 1);
                
            
        
        return result;
    
    
    

        此算法的时间复杂度为: o(n), 空间复杂度为o(m), m为子串的长度。

最长子串不重复问题(中等)

题目

        给定一个字符串,找出字符串中最长的不重复的子串,并返回它的长度。

思路一

        使用暴力循环破解的方式,通过两次循环,每次取出子串进行判断子串是否是不重复的,然后不断的比较之前的不重复的子串长度,最后返回。

        此方式的时间复杂度是大于o(n2), 耗时比较久,不可取。

思路二

        滑动窗口思想。

        1) 定义左指针和右指针,左指针和右指针之间的窗口为滑动窗口。

        2)  右指针每移动一位时,将不重复的字符添加到 Set集合里,Set集合用来判断是否出现重复字符。

        3) 当右指针开始遇到重复的字符时,那么计算一次长度,同时左指针右移动一位。

        4) 如果左指针移动一位后,右指针的下一位仍然重复,那么在右指针的位置再计算一次长度,同时左指针向右移动一位。

package leetcode100;

import java.util.HashSet;
import java.util.Set;

/**
 * @decription:
 * @date: 2021/7/20 17:29
 * 给定⼀个字符串,找到没有重复字符的最⻓⼦串,返回它的⻓度。
 */
public class MaxNotRepeatStrProblem 


    public static void main(String[] args) 
        String str = "asjfpoajfeskahfiasfjoj";
        Integer length = findMaxLengthNotRepeatStr(str);
        System.out.println("长度为:" + length);
    

    /**
     * 实现思路: 每次取出来字符串并判断不重复后,后续判断只需要判断最后一个字符不在之前的字符串里即可,否则滑动窗口。
     *  将已有的字符串存在hash里,hash的判断时间复杂度为o(1), 那么总的时间复杂度为o(n)。
     * @param str
     * @return
     */
    private static Integer findMaxLengthNotRepeatStr(String s) 
        int len=s.length();
        int ans=0;
        int right=-1;
        Set<Character> set=new HashSet<>();
        for(int i=0;i<len;i++)
             if(i!=0)
                set.remove(s.charAt(i-1));
            
           while(right+1<len && ! set.contains(s.charAt(right+1)))
                set.add(s.charAt(right+1));
                   right++;
            
            ans=Math.max(ans,right-i+1);
        
        return ans;
    

提交结果:

以上是关于Leetcode53.最大子串和(简单)76.最小子串覆盖(困难)3. 最长子串不重复问题(中等)的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode53.最大子串和(简单)76.最小子串覆盖(困难)3. 最长子串不重复问题(中等)

Leetcode53.最大子串和(简单)76.最小子串覆盖(困难)3. 最长子串不重复问题(中等)

Leetcode 53 最大子串和

53最大子序和.py

leetcode-Maximum Subarray-53

Leetcode 76.最小覆盖子串