杭电-1159Common Subsequence(LCS)

本文详细解析了最长公共子序列问题,提供了一个求解模板,并通过具体实例展示了如何应用该模板解决实际问题。

Common Subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 29563    Accepted Submission(s): 13312


Problem Description
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, ..., xm> another sequence Z = <z1, z2, ..., zk> is a subsequence of X if there exists a strictly increasing sequence <i1, i2, ..., ik> of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = <a, b, f, c> is a subsequence of X = <a, b, c, f, b, c> with index sequence <1, 2, 4, 6>. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
 

Sample Input
abcfbc abfcab programming contest abcd mnp
 

Sample Output
4 2 0


最长公共子序列模板题!


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[1001][1001];//数组大点不然re
int main()
{
	int m,n,i,j;
	char s1[1000],s2[1000];
	while(scanf("%s%s",s1+1,s2+1)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		int len1=strlen(s1+1);
		int len2=strlen(s2+1);
		for(i=1;i<=len1;i++) 
		{
			for(j=1;j<=len2;j++)
			{
				if(s1[i]==s2[j])
				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;
}



<think>我们要求解两个字符串的最长公共子序列(LongestCommonSubsequence,LCS)的长度。动态规划是解决LCS问题的经典方法。定义dp[i][j]为字符串text1的前i个字符和字符串text2的前j个字符的最长公共子序列的长度。状态转移方程如下:如果text1[i-1]==text2[j-1](因为字符串索引从0开始,所以i-1和j-1是当前字符),那么dp[i][j]=dp[i-1][j-1]+1。否则,dp[i][j]=max(dp[i-1][j],dp[i][j-1])。注意:我们通常将dp数组的行数和列数分别设为text1长度+1和text2长度+1,以便处理空序列的情况。初始化:dp[0][j]=0(text1的前0个字符与text2的前j个字符的LCS为0),dp[i][0]=0(同理)。最后,dp[len1][len2]即为所求。下面我们用C++实现该算法。</think># 最长公共子序列(Longest Common Subsequence)问题的 C++ 实现 最长公共子序列(LCS)是动态规划中的经典问题,用于求解两个字符串的最长公共子序列长度。以下是完整的 C++ 实现: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int longestCommonSubsequence(string text1, string text2) { int m = text1.length(); int n = text2.length(); // 创建动态规划表,大小为(m+1) x (n+1) vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); // 填充DP表 for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1[i - 1] == text2[j - 1]) { // 当前字符匹配,LCS长度加1 dp[i][j] = dp[i - 1][j - 1] + 1; } else { // 当前字符不匹配,取左方或上方的最大值 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; } int main() { string s1, s2; // 读取输入直到文件结束 while (cin >> s1 >> s2) { int result = longestCommonSubsequence(s1, s2); cout << result << endl; } return 0; } ``` ## 算法解析 ### 动态规划状态定义 定义二维数组 `dp[i][j]` 表示: - `text1` 的前 `i` 个字符 - `text2` 的前 `j` 个字符 的最长公共子序列长度[^2]。 ### 状态转移方程 $$ dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } text1[i-1] = text2[j-1] \\ \max(dp[i-1][j], dp[i][j-1]) & \text{otherwise} \end{cases} $$ ### 关键步骤 1. **初始化**:创建大小为 $(m+1) \times (n+1)$ 的 DP 表,第一行和第一列初始化为 0(空序列的 LCS 为 0) 2. **填充表格**: - 当前字符匹配时:`dp[i][j] = dp[i-1][j-1] + 1` - 当前字符不匹配时:`dp[i][j] = max(dp[i-1][j], dp[i][j-1])` 3. **结果提取**:右下角元素 `dp[m][n]` 即为最终结果 ### 复杂度分析 - **时间复杂度**:$O(m \times n)$,需要遍历两个字符串的所有字符组合 - **空间复杂度**:$O(m \times n)$,使用二维 DP 表存储中间结果 ## 示例测试 输入: ``` abcde ace abc abcd hello world ``` 输出: ``` 3 // "ace" 是 "abcde" 和 "ace" 的 LCS 3 // "abc" 是 "abc" 和 "abcd" 的 LCS 1 // 'o' 或 'l' 是 "hello" 和 "world" 的 LCS ``` ## 优化空间复杂度 对于超长字符串,可使用滚动数组优化空间至 $O(\min(m, n))$: ```cpp int optimizedLCS(string text1, string text2) { if (text1.length() < text2.length()) swap(text1, text2); int m = text1.length(), n = text2.length(); vector<int> dp(n + 1, 0); for (int i = 1; i <= m; i++) { int prev = 0; for (int j = 1; j <= n; j++) { int temp = dp[j]; if (text1[i-1] == text2[j-1]) { dp[j] = prev + 1; } else { dp[j] = max(dp[j], dp[j-1]); } prev = temp; } } return dp[n]; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值