给定字符串,求其最长不重复子串

原文:https://blog.csdn.net/z69183787/article/details/103291318

问题描述:
输入:abdca 返回:abdc

方法一:
暴力解析:遍历出给定字符串的所有子串,判断其中是否有重复字符,没有则记录长度,与下一次也无重复字符的子串比较长度,最长的即为所求。

代码:

public static String noDuplicate(String str) {
        if(str==null||str.length()==0){
            return null;
        }
        Set<String> set = new HashSet<>();
        String result = "";
        System.out.println("length:" + str.length());
        for (int i = 0; i < str.length(); i++) {
            for (int j = i + 1; j <= str.length(); j++) {
                String s = str.substring(i, j);
                set.add(s);
            }
        }
        int max = 0;
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            LinkedHashSet<String> setchar = new LinkedHashSet<>();
            String  st = iterator.next().toString();
            for (int a = 0; a < st.length(); a++) {
                setchar.add(String.valueOf(st.charAt(a)));
            }
            if(setchar.size()==st.length()){
                int len = st.length();
                if(max<len){
                    max = Math.max(max, len);
                    result = st;
                }
            }
        }
        System.out.println(max);
        return result;
    }

暴力法的时间复杂度为:n2+n2===>2n2 即O(n2) 
时间复杂度排序: 
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)

方法二:
滑动窗口:保证窗口中都是不重复的子串实现 _有回溯

public static String getMaxsubHuisu(String s) {
        if (s == null || s.length() == 0) {
            return null;
        }
 
        int start = 0;//滑动窗口的开始值
        int maxlen = 0;
        int len = 0;
        int startMaxIndex = 0;//最长子串的开始值
        Map<Character, Integer> map = new HashMap<>();//存储窗口内字符跟位置
        int i;
        for (i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            Integer value = map.get(ch);
            if (map.containsKey(ch)) {//map中包含字符,则出现重复字符
                start = value + 1;//下一次开始的位置是,存在map中重复字符的下一个位置
                len = 0;//重新开始新的窗口,len置为0
                map = new HashMap<>();//map置空
                i=value;//下次从重复的值开始回溯
            } else {
                map.put(ch, i);//不存在重复的,就存入map
                len++;//每次进来长度自增1
                if (len > maxlen) {//如果当前的窗口长度>最长字符串则,更新最长串,跟最长子串开始位置
                    maxlen = len;
                    startMaxIndex = start;
                }
            }
        }
        return s.substring(startMaxIndex, (startMaxIndex + maxlen));\\截取字符串,substring为左闭右开
    }

实现 _无回溯

public static String getMaxsub(String s) {
        if (s == null || s.length() == 0) {
            return null;
        }
 
        int start = 0;//滑动窗口的开始值
        int maxlen = 0;
        int len = 0;
        int startMaxIndex = 0;//最长子串的开始值
        Map<Character, Integer> map = new HashMap<>();
        int i;
        for (i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            Integer value = map.get(ch);
            if (value != null && value >= start) {//map中存在在字符,且字符的位置在窗口范围内,即字符有效
                start = value + 1;//下次开始的位置为,重复字符的下一个位置
                len = i - value;//长度为当前的遍历字符的位置-重复字符的位置
            } else {
                len++;//若不存在,则字符+1
                if (len > maxlen) {//若当前窗口的长度>最长子串的位置,则更新最长子串的长度跟最长子串的起始位置
                    maxlen = len;
                    startMaxIndex = start;
                }
            }
            map.put(ch, i);//无论存在不存在重复字符,都要把遍历的 字符存入map中,重复的则覆盖
        }
        return s.substring(startMaxIndex, (startMaxIndex + maxlen));
 
 
    }

无回溯_具体过程示意图

 

 

时间复杂度
有回溯:最好情况无回溯,时间复杂度O(n),最坏情况,每次都回溯O(2n),平均值为O(3/2 *n) 
无回溯:O(n) 
在特别长的字符串情况下,会有差异

小结:
1.第一步对输入的值做判断,不可忘; 
2. 滑动窗口法的思路很重要,先用一个窗口限定字符,每次判断字符是否重复,记录位置,最长子串开始位置跟长度,为之后截取字符串做准备 
3. 要想明白过程,出现重复字符的时候需要做的,不出现重复时候需要做的: 
出现重复的 时候:更新窗口位置,当前窗口的长度。 
不出现重复的时候:当前长度自增,判断更新最长子串长度与起始位置,存字符。 
边界值出错的时候,要重缕思路,不能只是去试,要看思路边界上哪里有问题

posted @ 2020-10-12 10:43  隔壁w王叔叔  阅读(590)  评论(0编辑  收藏  举报