1. 程式人生 > >求字元最長且不重複的子串長度(暴力以及滑動視窗解決)

求字元最長且不重複的子串長度(暴力以及滑動視窗解決)

題目:

給定一個字串,找出不含有重複字元的最長子串的長度。

示例:

給定 "abcabcbb" ,沒有重複字元的最長子串是 "abc" ,那麼長度就是3。

給定 "bbbbb" ,最長的子串就是 "b" ,長度是1。

給定 "pwwkew" ,最長子串是 "wke" ,長度是3。請注意答案必須是一個子串"pwke" 是 子序列  而不是子串。

暴力解決方法:

首先枚舉出所有的子串,用star記錄子串的起始點,end記錄子串的末尾。枚舉出所有的子串之後,開始檢查子串中是否存在重複的字元,若存在則該子串不符合要求,若該子串中不存在重複的字元,那麼將該子串的長度與之前的最大長度相比,若比最大長度還長,那麼更新最大長度的值。最後返回最長長度。

要點:

(1)枚舉出所有的子串 (2)要求該子串中沒有重複的字元,若存在重複的字元則不符合題目要求(3)更新不重複子串中的長度

程式碼實現(c語言)
/*更新最長長度*/
int Max(int a, int b)
{
    int max=0;
    return max = a>b?a:b;
}
/*檢查字元ch是否存在在陣列st中*/
int contain(char *st, char ch)
{
    int len = strlen(st);
    for(int i=0; i<len; i++)
    {
        if(st[i]==ch)
        {
            return 1;
        }
    }
    return 0;
}
int judge(char *s, int star, int end)
{
    int len = strlen(s);
    int k = 0;
    char *st;
    st = (char *)malloc(end+1-star);
    /*判斷子串是否重複,需要一個數組st來儲存之前的字元,然後用當前字元與之前字元做比較,若不重複,將當前字元加入st陣列中為下次比較做準備*/
    for(int i=star; i<end; i++)
    {
        if(!contain(st,s[i]))
        {
            st[k++] = s[i];
        }
        else{
            return 0;
        }

    }
    return 1;

}

int lengthOfLongestSubstring(char* s) {
    int len = strlen(s);
    int max = 0;
    /*枚舉出所有的子串*/
    for(int i=0; i<len; i++)
    {
        for(int j=i+1; j<=len; j++)
        {
            if(judge(s,i,j))//若該子串不重複
            {
                max = Max(max, j-i);//j-i為當前子串的長度,更新最長長度
            }
        }
    }
    return max;
    
}

c++實現:

class Solution {
public:
    int Max(int a, int b)
    {
        int max = a;
       return max = a>b?a:b;
    }
    bool judge(string s, int star, int end)
    {
        string st="";
        for(int i=star; i<end; i++)
        {
            if(st.find(s[i])==st.npos)//該元素在子串中不重複
            {
                st.insert(st.end(),s[i]);
            }
            else{
                return false;
            }
        }
        return true;
    }
    int lengthOfLongestSubstring(string s) {
        int max = 0,i=0,j=0;
        int len = s.size();
        for(int i=0; i<len; i++)
        {
            for(int j=i+1; j<=len; j++)
            {
                if(judge(s,i,j))
                {
                    max = Max(max, j-i);
                }
            }
        }
        return max;
    }
};

滑動視窗解決方法:

暴力解決方法,通常要反覆的檢視子串中是否有重複的字元。那麼假設從i到j-1之間的子串中沒有重複的字元了,那麼只需要堅持S[j]的元素是否與i到(j-1)中的重複。要檢查一個字元是否已經在子字串中,我們可以檢查整個子字串,這將產生一個複雜度為 O(n^2) 但是利用滑動視窗的話,可以用時間複雜度為O(1)來完成子串中的檢查。

視窗通常是在陣列/字串中由開始和結束索引定義的一系列元素的集合,即 [i,j)[i,j)(左閉,右開)。而滑動視窗是可以將兩個邊界向某一方向“滑動”的視窗。例如,我們將 [i,j)[i,j)向右滑動 11 個元素,則它將變為 [i+1,j+1)[i+1,j+1)(左閉,右開)。所以在該問題中,同時設定索引i,j 最開始兩個索引的位置是相同的,都在下標為0處。然後j開始往右滑動,滑動前要檢查,j此時所在的位置的元素是否和之前的字元重複,若不重複的話,將s[j]加入到st中,並且更新最長子串長度。若此時j所在的位置的元素和之前子串中的重複,此時j不再滑動,此時刪除st中的s[i],並且滑動i,使得st的範圍一直在i和j之間。

程式碼實現(C++)

class Solution {
public:
    int Max(int a, int b)
    {
        int max = a;
        if(b > max)
        {
            return b;
        }
        else
        {
            return a;
        }
    }
    int lengthOfLongestSubstring(string s) {
        int max = 0,i=0,j=0;
        int len = s.size();
        string st="";
        while(i<len && j<len)
        {
            //j在子串中不重複
           if(st.find(s[j])==st.npos)
           {
               st.insert(st.end(),s[j]);
               j++;
               max = Max(max, j-i);
           }
           else
           {
               st.erase(st.find(s[i]),1);
               i++;
            }
        }
        return max;
        
    }
};