POJ1458 最长公共子序列 经典DP

来源:互联网 发布:边缘性行为是什么 知乎 编辑:程序博客网 时间:2024/05/29 09:13

题目不说了,学习DP时候经典的例子。如果现在有两个序列X = {X1,X2,......,Xm}和Y = {Y1,Y2,.......,Yn},他们的LCS是Z = {Z1,Z2,.....,Zk}。下面分两种情况讨论:

1) 如果Xm = Yn,那么一定有Zk = Xm = Yn,即Xm与Yn相等时他们一定是LCS结尾的那个元素。LCS(X, Y) = LCS(X', Y') + Xm。

2)如果Xm != Yn,那么Zk != Xm 和Zk != Yn至少有一个成立。则LCS(X, Y) = max{ LCS(X', Y), LCS(X, Y')},其中X'和Y'是X和Y剔除最右边元素而得到的序列。

从上面的讨论可以得到状态转移方程:

图是我盗的,C[i, j]表示X序列前i项和Y序列前j项的LCS的长度。

下面是代码,可以用滚动数组优化下空间复杂度。

#include <stdio.h>#include <string.h>int max(int a, int b){return a > b ? a : b;}int main(){char str1[210], str2[210];int len1,len2;while(scanf("%s %s", str1, str2) != EOF){len1 = strlen(str1);len2 = strlen(str2);int dp[210][210], i, j;for(i = 0; i < 210; i++){dp[i][0] = 0;dp[0][i] = 0;}for(i = 1; i <= len1; i++){for(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] = max(dp[i-1][j], dp[i][j-1]);}}} printf("%d\n", dp[len1][len2]);}return 0;}

/*如果只计算长度空间复杂度可以优化到O(N),因为填矩阵时候每次存当前这行和上面那行就够了。除非你要得到最优子序列。*/#include <stdio.h>#include <string.h>int max(int a, int b){return a > b ? a : b;}int main(){char str1[210], str2[210];int len1,len2;while(scanf("%s %s", str1, str2) != EOF){len1 = strlen(str1);len2 = strlen(str2);int dp[2][210], i, j;for(i = 0; i < 201; i++){dp[0][i] = 0;dp[1][i] = 0;}for(i = 1; i <= len1; i++){for(j = 1; j <= len2; j++){ if(str1[i-1] == str2[j-1]){dp[i %2][j] = dp[(i-1) %2][j-1] + 1;}else{dp[i %2][j] = max(dp[(i-1) %2][j], dp[i %2][j-1]);}}} printf("%d\n", dp[len1 %2][len2]);}return 0;}

其实这篇文章讲的挺详细的,尤其是怎么求LCS还有优化的细节。http://blog.csdn.net/v_JULY_v/article/details/6110269#t1


0 0