最长公共子序列,字串

版权声明:版权归Ordinarv所有 https://blog.csdn.net/ordinarv/article/details/84723198

首先说明子序列和子串的区别

子串是必须连续的(相邻),是特殊的子序列。

对于一般的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];

猜你喜欢

转载自blog.csdn.net/ordinarv/article/details/84723198