1. 程式人生 > >最長公共子序列,字串

最長公共子序列,字串

首先說明子序列和子串的區別

子串是必須連續的(相鄰),是特殊的子序列。

對於一般的LCS問題,都屬於NP問題。當數列的量為一定的時,都可以採用動態規劃去解決。

最直接的解法自然是找出兩個字串的所有子字串進行比較看他們是否相同,然後取得相同最長的那個。對於一個長度為n的字串,它有n(n+1)/2 個非空子串。所以假如兩個字串的長度同為n,通過比較各個子串其演算法複雜度大致為O(n4)。這還沒有考慮字串比較所需的時間。簡單想想其實並不需要取出所有的子串,而只要考慮每個子串的開始位置就可以,這樣可以把複雜度減到O(n3)

這個問題使用動態規劃法的契機:有重疊的子問題。進而可以通過空間換時間,讓複雜度優化到O(n2)

,代價是空間複雜度從O(1)一下子提到了O(n2)

比如再比較以ij分別為起始點字串時,有可能會進行i+1j+1以及i+2j+2位置的字元的比較;而在比較i+1j+1分別為起始點字串時,這些字元又會被比較一次了。也就是說該問題有非常相似的子問題,而子問題之間又有重疊,這就給動態規劃法的應該提供了契機。

 

該解法的思路就如前所說,以字串中的每個字元作為子串的端點,判定以此為開始的子串的相同字元最長能達到的長度。其實從表層上想,這個演算法的複雜度應該只有O(n2)因為該演算法把每個字元都成對相互比較一遍,但關鍵問題在於比較兩個字串的效率並非是O(1),這也導致了實際的時間複雜度應該是滿足

Ω(n2)O(n3)


因為要根據前一位的dp值來確定當前位的具體值,所以具體處理0位時有不同寫法,第二種更簡介一些

把0前一位陣列下標非法,故字串元素為1開始 dp0為0;

最長公共子序列(DP)

int lcs(string str1, string str2) {  //核心程式碼
    int len1 = str1.length();  
    int len2 = str2.length();  
    int result = 0;     //記錄最長公共子串長度    
    int c[len1 + 1][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;  
} 

最長公共子串

    scanf("%s%s", Str1, Str2);
    Len1 = strlen(Str1), Len2 = strlen(Str2);
    for (int i = 1; i <= Len1; ++i) {
        for (int j = 1; j <= Len2; ++j) {
            if (Str1[i - 1] == Str2[j - 1]) {
                Dp[i][j] = Dp[i - 1][j - 1] + 1;
            }
            else {
                Dp[i][j] = 0;
            }
        }
    }
    int ans =Dp[Len1][Len2];