线性DP-最长公共子序列

题意

给定两个长度分别为 N 和 M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。

输入格式

第一行包含两个整数 N 和 M。

第二行包含一个长度为 N 的字符串,表示字符串 A。

第三行包含一个长度为 M 的字符串,表示字符串 B。

字符串均由小写字母构成。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N,M≤1000

输入样例:

4 5
acbd
abedc

输出样例:

3

分析

 

 

 

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

char a[N],b[N];
int dp[N][N];
int n, m;

int main() {
	cin >>n>>m;
	cin>>a+1>>b+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			if(a[i]==b[j])
				dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
		}
	}
	cout<<dp[n][m];
	return 0;
}

### 最长公共子序列 (LCS) 算法实现与解释 #### 动态规划方法概述 最长公共子序列(Longest Common Subsequence, LCS)问题是经典的计算机科学问题之一,通常用于比较两组数据的相似度。其核心思想是利用动态规划来解决复杂性较高的字符串匹配问题。 对于给定的两个序列 \(X\) 和 \(Y\),定义 \(c[i][j]\) 表示序列 \(X[1..i]\) 和 \(Y[1..j]\) 的最长公共子序列的长度[^3]。通过构建二维表并逐步填充其中的值,可以最终得到整个问题的解。 --- #### 动态规划状态转移方程 设 \(X = \{x_1, x_2, ..., x_m\}\),\(Y = \{y_1, y_2, ..., y_n\}\),则有如下递推关系: - 如果 \(x_i = y_j\),那么 \(c[i][j] = c[i-1][j-1] + 1\); - 否则,取两者中的较大者: \(c[i][j] = \max(c[i-1][j], c[i][j-1])\); 边界条件为当任意一方为空串时,公共子序列长度为零,即 \(c[i][0] = 0\) 或 \(c[0][j] = 0\)。 此公式描述了如何基于前一步的结果计算当前步的状态,从而避免重复运算,显著提高效率。 --- #### Python 实现代码 以下是使用 Python 编写的 LCS 算法的具体实现: ```python def lcs(X, Y): m = len(X) n = len(Y) # 创建一个(m+1)x(n+1)大小的表格存储中间结果 C = [[0]*(n+1) for _ in range(m+1)] # 填充表格 for i in range(1, m+1): for j in range(1, n+1): if X[i-1] == Y[j-1]: C[i][j] = C[i-1][j-1] + 1 else: C[i][j] = max(C[i-1][j], C[i][j-1]) # 反向追踪找到具体的LCS序列 result = [] i, j = m, n while i > 0 and j > 0: if X[i-1] == Y[j-1]: result.append(X[i-1]) i -= 1 j -= 1 elif C[i-1][j] >= C[i][j-1]: i -= 1 else: j -= 1 return ''.join(reversed(result)), C[m][n] # 测试用例 if __name__ == "__main__": X = "ABCBDAB" Y = "BDCAB" sequence, length = lcs(X, Y) print(f"LCS 序列为: {sequence}, 长度为: {length}") ``` 上述程序不仅返回了最长公共子序列的实际内容,还给出了它的长度。这种方法的时间复杂度为 \(O(m \times n)\)[^2],空间复杂度同样如此。 --- #### Java 实现代码 如果偏好于其他编程语言,比如 Java,则可以用类似的逻辑重写算法: ```java public class LongestCommonSubsequence { public static String lcs(String X, String Y){ int m = X.length(); int n = Y.length(); // 构建DP矩阵 int[][] dp = new int[m+1][n+1]; // 计算dp数组 for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if(X.charAt(i-1)==Y.charAt(j-1)) { dp[i][j]=dp[i-1][j-1]+1; } else{ dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]); } } } // 追溯路径获取LCS StringBuilder sb=new StringBuilder(); int index=dp[m][n]; int temp=index; char[] lcsStr =new char[index+1]; lcsStr[index]='\0'; int i=m,j=n; while(i>0 && j>0){ if(X.charAt(i-1)==Y.charAt(j-1)){ lcsStr[index-1]=X.charAt(i-1); i--; j--; index--; }else if(dp[i-1][j]>=dp[i][j-1]){ i--; }else{ j--; } } return sb.append(lcsStr).toString().trim(); } public static void main(String args[]){ System.out.println(LongestCommonSubsequence.lcs("ABCBDAB", "BDCAB")); } } ``` 这段代码展示了另一种高级语言下的解决方案,并且验证了跨平台的一致性和可行性[^2]。 --- #### 性能优化方向 尽管标准版本已经非常有效率,但在某些特殊情况下仍可能考虑进一步改进策略,例如压缩空间需求至线性级别或者采用二分查找加速特定模式识别等技术手段。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值