1. 程式人生 > >值最大子串,最長無重複子串,最長無重複子序列,最長公共子串,最長公共子序列解法及程式碼

值最大子串,最長無重複子串,最長無重複子序列,最長公共子串,最長公共子序列解法及程式碼

1.值最大子串

比如{5,-3,4,2}的最大子序列就是 {5,-3,4,2},它的和是8,達到最大;而 {5,-6,4,2}的最大子序列是{4,2},它的和是6。

思路:看子串和是否大於0,大於0繼續加,小於0就從當前開始。

int maxSubSum(const vector<int> & arr,int &begin,int &end){
    int maxSum=0;
    int currSum=0;
    int newbegin=0;
    for(int i=0;i<arr.size();++i){
        currSum+=arr[i];
        if(currSum>maxSum){
            maxSum=currSum;
            begin=newbegin;
            end=i;
        }
        if(currSum<0){
            currSum=0;
            newbegin=i+1;
        }
    }
    return maxSum;
}

2. 最長無重複子串

子串(substring)——在字串中是連續的

子序列(subsequence)——在字串中可以不連續,也可以連續

思路:藉助一個雜湊表儲存每個字元出現的位置

A)如果第i個字元之前沒有出現過,那麼f(i) = f(i-1)+1;

B)如果第i個字元之前出現過,記第i個字元和它上出現在字串中的位置的距離,記為d。如果d>f(i-1),那麼仍然有f(i) = f(i-1)+1;如果d<=f(i-1),則說明第i個字元上次出現在f(i-1)對應的不重複字串之內,那麼這時候更新 f(i) = d

class Solution {
public:
    int longestSubstringWithoutDuplication(const string& str) {
        int curLength = 0;
        int maxLength = 0;
        int* position = new int[26];
        for (int i = 0; i < 26; ++i)
            position[i] = -1;
        for (int i = 0; i < str.length(); ++i) {
            int prevIndex = position[str[i] - 'a'];
            if (prevIndex < 0 || i - prevIndex > curLength)
                ++curLength;
            else {
                if (curLength > maxLength)
                    maxLength = curLength;

                curLength = i - prevIndex;
            }
            position[str[i] - 'a'] = i;
        }
        if (curLength > maxLength)
            maxLength = curLength;
        delete[] position;
        return maxLength;
    }
};

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int m[256]={0},maxlen=0,left=0;
        for(int i=0;i<s.length();++i)
        {
            if(m[s[i]]==0 || m[s[i]]<left)   //從未出現過和未在當前滑動視窗內
                maxlen = max(maxlen,i-left+1);
            else 
            {
                left = m[s[i]];
            }
            m[s[i]] = i+1;
        }
        return maxlen;
    }
};

3.最長無重複子序列

思路:沒用雜湊表,只需要看之前是否出現過。也可以用個雜湊表更快。

int lengthOfLongestSubstring(string s) {
        int max_len = 0, flag;
        for(int i = 0; i < s.length(); i++){
            flag = 1;
            for(int j = 0; j < i; j++){
                if(s[i] == s[j]){
                    flag = 0;
                    break;
                }
            }
            if(flag){
                max_len++;
            }
        }
        return max_len;
    }

4.最長公共子序列

子串(substring)——在字串中是連續的

子序列(subsequence)——在字串中可以不連續,也可以連續

示例:比如cnblogs和belong兩個字串,bo, bg, lg,blo,blog都在兩個 字串中出現過, 並且出現順序一致,求最長公共子序列 為blog。最長公共子串為lo。

思路:

暴力解法
假設 m<n, 對於母串X,我們可以暴力找出2的m次方個子序列,然後依次在母串Y中匹配,演算法的時間複雜度會達到指數級O(n∗2的m次)。顯然,暴力求解不太適用於此類問題。
動態規劃
假設Z=<z1,z2,⋯,zk>是X與Y的LCS, 我們觀察到
如果Xm=Yn,則Zk=Xm=Yn,有Zk−1是Xm−1與Yn−1的LCS;
如果Xm≠Yn,則Zk是Xm與Yn−1的LCS,或者是Xm−1與Yn的LCS。
因此,求解LCS的問題則變成遞迴求解的兩個子問題。但是,上述的遞迴求解的辦法中,重複的子問題多,效率低下。改進的辦法——用空間換時間,用陣列儲存中間狀態,方便後面的計算。這就是動態規劃(DP)的核心思想了。
DP求解LCS
用二維陣列c[i][j]記錄串x1x2⋯xi與y1y2⋯yj的LCS長度,則可得到狀態轉移方程

int lcs(String str1, String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    int **c = new int*[len1+1];
    for(int i=0;i<len1+1;++i)
        c[i]=new int[len2+1];
    for (int i = 0; i <= len1; i++) {
        for( int j = 0; j <= len2; j++) {
            if(i == 0 || j == 0) {
                c[i][j] = 0;
            } else if (str1[i-1] == str2[j-1]) {
                c[i][j] = c[i-1][j-1] + 1;
            } else {
                c[i][j] = max(c[i - 1][j], c[i][j - 1]);
            }
        }
    }
    return c[len1][len2];
}

5.最長公共子串

思路:同樣可以用DP來解決。定義陣列的儲存含義對於後面推導轉移方程顯得尤為重要,糟糕的陣列定義會導致異常繁雜的轉移方程。考慮到子串的連續性,將二維陣列c[i][j]用來記錄具有這樣特點的子串——結尾同時也為為串x1x2⋯xi與y1y2⋯yj的結尾——的長度。
得到轉移方程:

最長公共子串的長度為 max(c[i,j]), i∈{1,⋯,m},j∈{1,⋯,n}。

int longsubstr(String str1, String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    int result = 0;     //記錄最長公共子串長度
    int **c = new int*[len1+1];
    for(int i=0;i<len1+1;++i)
        c[i]=new int[len2+1];
    for (int i = 0; i <= len1; i++) {
        for( int j = 0; j <= len2; j++) {
            if(i == 0 || j == 0) {
                c[i][j] = 0;
            } else if (str1[i-1] == str2[j-1]) {
                c[i][j] = c[i-1][j-1] + 1;
                result = max(c[i][j], result);
            } else {
                c[i][j] = 0;
            }
        }
    }
    return result;
}

參考部落格:

https://www.cnblogs.com/zhaogl/p/6364654.html

https://blog.csdn.net/u012102306/article/details/53184446

https://blog.csdn.net/wangdd_199326/article/details/76464333