##[$AcWing$ $897$. 最长公共子序列](https://www.acwing.com/problem/content/description/899/) ### 一、题目描述 给定两个长度分别为 $N$ 和 $M$ 的字符串 $A$ 和 $B$,求既是 $A$ 的子序列又是 $B$的子序列的字符串 **长度最长** 是多少。 **输入格式** 第一行包含两个整数 $N$ 和 $M$。 第二行包含一个长度为 $N$ 的字符串,表示字符串 $A$。 第三行包含一个长度为 $M$ 的字符串,表示字符串 $B$。 字符串均由小写字母构成。 **输出格式** 输出一个整数,表示最大长度。 **数据范围** $1≤N,M≤1000$ **输入样例**: ```cpp {.line-numbers} 4 5 acbd abedc ``` **输出样例**: ```cpp {.line-numbers} 3 ``` ### 二、$LCS$问题分析 定义$f[i][j]$是$a[]$以$i$结尾,$b[]$以$j$结尾的**最长公共子序列长度** > 没有说$a[i]$或者$b[j]$一定要出现在最长公共子序列当中!这个最长公共子序列,可能是$a[]$和$b[]$的一些前序组成的,$a[i],b[j]$也可能没有对结果产生贡献。 * 当$a[i]==b[j]$时,看一下两个字符串的前序,发现在少了$a[i],b[j]$后,转化为子问题$f[i-1][j-1]$,问题转化为$$f[i][j]=f[i-1][j-1]+1$$ * 当$a[i] \neq b[j]$时: * 如果$a[i]$不产生贡献,那么把它干掉$f[i-1][j]$ * 如果$b[j]$不产生贡献,那么把它干掉$f[i][j-1]$ * 如果两个都没有贡献,那么就是$f[i-1][j-1]$ ![](https://img2020.cnblogs.com/blog/8562/202110/8562-20211020161132007-1452049398.png) ### 三、实现代码 ```cpp {.line-numbers} #include using namespace std; const int N = 1010; int n, m; char a[N], b[N]; int f[N][N]; int main() { // 递推式出现f[i-1][j-1],如果i,j从0开始会出现负值,所以下标从1开始 cin >> n >> m >> a + 1 >> b + 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1; else f[i][j] = max(f[i - 1][j], f[i][j - 1]); printf("%d", f[n][m]); return 0; } ```