1. 程式人生 > 實用技巧 >給定字串,求其最長不重複子串

給定字串,求其最長不重複子串

原文: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. 要想明白過程,出現重複字元的時候需要做的,不出現重複時候需要做的:
出現重複的 時候:更新視窗位置,當前視窗的長度。
不出現重複的時候:當前長度自增,判斷更新最長子串長度與起始位置,存字元。
邊界值出錯的時候,要重縷思路,不能只是去試,要看思路邊界上哪裡有問題