求最大不重複子串(Java)

一個經典問題,就是求字符串中不包含重複字符的最大子串。如果有多個這樣的子串,則輸出第一個。例:str=”abxacsvada”,最大不重複子串爲:“bxacsv”。
我的思路其實也就是從頭比較到尾來找,只是中間加了一些判斷條件進行了優化。具體流程(先轉化成char[] ch):
1、假設該最長子串的首字符爲ch [i] (0<=i< ch.length),則找到ch[i]==ch[j] (i< j< ch.length).此時”j”也就確定了該子串有可能的最大長度了。
2、進一步確定該子串的長度,也就是要確定從ch[i]起,“j”長度的子串裏有沒有重複的字符,爲了區分,這裏定義int value=j。確認是否有ch[i+1]==ch[j] (i+1< j< value)。如果有,則進一步確定子串長度,再把此時的”j”賦值給value。
3、重複上訴2步驟直到“i+n”==value.此時的value也就是以ch[i]爲起點的最大不重複子串的長度值。
代碼如下:

import java.util.Scanner;
public class Main {

    static String calDuplicateString(String str) {//分別以0-ch.length的字符爲首字符尋找最大不重複子串
        char[] ch=str.toCharArray();//轉化成char數組
        int max=0;//記錄最大不重複子串的長度
        int value;//記錄以ch[i]爲首字符的最大不重複子串的長度
        int k=0;//記錄最大不重複子串的起始下標
        for(int i=0;i<ch.length;i++){//i表示起始字符的下標

            if((i-1)<(max+k) && i>0 && ch[i-1]!=ch[max+k])//優化算法①
                continue;

            value=search(ch,i,max+k);//以ch[i]爲首字符,尋找最大不重複子串,返回長度
            if(max<value){//如果此時得到的長度比原來得到的長度長,那就賦值給max,並且記錄下此時的首字符下標
                max=value;
                k=i;
            }
            if(max>=ch.length-i-1)//優化算法②
                break;
        }

        return new String(ch,k,max);//返回該最大子串

    }

    //該方法爲了尋找以ch[head]爲起始字符的最大子串,其中head表示起始字符的下標,left表示最開始需要比較的起始位置
    static int search(char[] ch,int head, int left) {
        int m=ch.length;//表示最大子串的最後一個字符的下標
        //上面思路對應的代碼就是這裏
        for(int i=head;i<m-1;i++)③
            for(int j=left;j<m;j++)
                if(ch[i]==ch[j]){//一旦出現相同字符,則後面的字符都不可能是該最大子串的了
                    m=j;
                    break;
                }
        return m-head;//返回最大子串的長度


    }

    //main函數測試
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);

        String _str;
        _str = in.nextLine();

        System.out.println(calDuplicateString(_str));

    }
}

原始思路就是暴力解法,中間我加了兩步優化,其實嚴格算三步。代碼中標號處爲優化的地方,逐一解釋:
①:假設String str=”abcdbacdjas”。當第一次尋找以第一個字符爲首字符的最長子串時,找的最長子串爲“abcd”,此時我們尋找以第二個字符爲首字符的最大子串,分析一下:
對於第一次找到的結果來看,前四個字符是沒有重複字符的,並且說明第五個字符肯定與前四個字符中的某一個相同(不考慮到末尾的情況)。當且僅當第一個字符等於第五個字符時,第二個字符爲首字符的最大子串的長度纔有可能大於等於第一次找到的最大子串。因爲如果ch[0]!=ch[4],那麼肯定存在ch[i](0< i <4)==ch[4]。所以以第二個字符爲首字符的最大子串的長度不可能大於3了。所以得出三個判斷條件:
(i-1)<(max+k) && i>0 && ch[i-1]!=ch[max+k]
i>0:因爲第一次時沒有前面得出的最大子串。

ch[i-1]!=ch[max+k]:也就是上面分析得出的判斷式

(i-1)<(max+k):例如上面,所有分析都是比較前面得出的最大子串與第五個字符是否相等,那麼顯然不能比較大於第五個字符後的字符了。不然如果一直沒有尋找到大於原來的最大子串的最大子串,那麼max+k始終不變,而i-1已經>=max+k了,這樣就會continue掉很多不該continue的地方。

②:max>=ch.length-i-1:這個好理解,就是尋找以ch[i]爲首字符的最大子串時,如果ch[i]加上它後面的所有字符如果都不能大於之前得到最大子串的長度max時,那麼,後面的這些字符都沒有去尋找的必要了。

③:這裏是縮小了兩個循環的範圍,假設:在尋找以第一個字符爲首字符的最大子串時,因爲不知道最開始有多長,所以需要設爲ch.length,如果此時找到ch[0]==ch[5],那麼此時最大子串的長度就<=5了,所以此時還需要判斷ch[1]到ch[4]裏有沒有重複字符就可以了。而left是根據上一次得出的最大子串來確定的,上面已經①中分析過了。

上面代碼有可能還存在bug,畢竟我沒有把所有特殊情況都測試一遍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章