最长公共子序列(C++实现)
序列的子序列指序列中的一些元素被省略。给定一个序列 x=<x1,x2,…,xm> 及另一个序列 z=<z1,z2,…,zk>,若 x 的索引存在严格递增的序列 <i1,i2,…,ik>,则对于 j=1,2,…,k 及 xij=zj,z 都是 x 的子序列。
例如,z=<a,b,f,c> 的索引序列是 <1,2,4,6>,它是 x=<a,b,c,f,b,c> 的子序列。若 z 既是 x 的子序列,也是 y 的子序列,则称 z 是 x 和 y 的公共子序列。
给定两个序列 x 和 y,求 x 和 y 的最长公共子序列的长度。
1) xi=yj:求解 Xi-1 和 Yj-1 的最长公共子序列的长度加 1,dp[i][j]=dp[i-1][j-1]+1。
2) xi≠yj:可以把 xi 去掉,求解 Xi-1 和 Yj 的最长公共子序列的长度,或者把 yj 去掉,求解 Xi 和 Yj-1 的最长公共子序列的长度,取二者的最大值,dp[i][j]=max(dp[i][j-1],dp[i-1][j])。
例如,z=<a,b,f,c> 的索引序列是 <1,2,4,6>,它是 x=<a,b,c,f,b,c> 的子序列。若 z 既是 x 的子序列,也是 y 的子序列,则称 z 是 x 和 y 的公共子序列。
给定两个序列 x 和 y,求 x 和 y 的最长公共子序列的长度。
- 输入:每个测试用例都包含两个表示给定序列的字符串,序列以任意数量的空格隔开。
- 输出:对于每个测试用例,都单行输出最长公共子序列的长度。

算法设计
本题为最长公共子序列问题:- 状态表示:dp[i][j] 表示 x1..i 和 y1..j 的最长公共子序列的长度;
- 状态转移:两个序列中的字符 xi 和 yj 可能存在以下两种情况:
1) xi=yj:求解 Xi-1 和 Yj-1 的最长公共子序列的长度加 1,dp[i][j]=dp[i-1][j-1]+1。

2) xi≠yj:可以把 xi 去掉,求解 Xi-1 和 Yj 的最长公共子序列的长度,或者把 yj 去掉,求解 Xi 和 Yj-1 的最长公共子序列的长度,取二者的最大值,dp[i][j]=max(dp[i][j-1],dp[i-1][j])。

- 边界条件:dp[i][0]=0,dp[0][j]=0;
- 求解目标:dp[n][m],n、m分别为两个字符串的长度。
算法实现
#include <iostream> #include <string.h> #include <algorithm> using namespace std; #define maxn 100 //最长公共子序列,时间复杂度为O(nm) int dp[maxn][maxn]; //dp[i][j]表示s1[1..i]和s2[1..j]的最长公共子序列的长度 int main() { char s1[100],s2[100]; while(~scanf("%s%s",s1,s2)) { int len1=strlen(s1); int len2=strlen(s2); for(int i=0;i<=len1;i++) dp[i][0]=0; for(int j=0;j<=len2;j++) dp[0][j]=0; for(int i=1;i<=len1;i++) for(int j=1;j<=len2;j++) { for(int k=1;k<=len2;k++) { if(s1[i-1]==s2[j-1]) //字符串的下标实际上从0开始 dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i][j-1],dp[i-1][j]); } } printf("%d\n",dp[len1][len2]); } return 0; }运行结果为:
abcfbc abfcab
4