用二维 DP 求两串最长公共子序列,再用滚动数组把空间压到 O(m)。
OJ: roj
题目 ID: 1265
难度:普及
标签:DPLCS滚动数组
创建: 2026-01-14 22:00
更新: 2026-10-04 10:41
形式化题目
给定两个由大写字母组成的字符串
正解
思路
设
考虑最后一对字符
- 若
,这两个字符可以同时作为某个最长公共子序列的末尾字符, 因此 - 若
,则最优解不会同时使用这两个字符, 于是去掉其中一个得到较短前缀的最优解,取两者较大值:
边界为
样例 DP 表
以
| 0 | 1 B | 2 D | 3 C | 4 A | 5 B | 6 A | |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 A | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 2 B | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
| 3 C | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
| 4 B | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
| 5 D | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
| 6 A | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
| 7 B | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
表中加粗的
空间优化
递推式只依赖上一行
一轮结束后交换
代码
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-01-14 22:00
# update_at: 2026-10-04 10:41
import sys
def solve() -> None:
"""最长公共子序列:滚动数组 DP。"""
data = iter(sys.stdin.buffer.read().split())
x, y = next(data), next(data) # 两行序列,按 next() 顺序消费(字符数据,不转 int)
n, m = len(x), len(y)
prev = [0] * (m + 1) # 上一行
cur = [0] * (m + 1) # 当前行
for i in range(1, n + 1):
xi = x[i - 1]
for j in range(1, m + 1):
if xi == y[j - 1]:
cur[j] = prev[j - 1] + 1 # 匹配,继承左上 +1
else:
cur[j] = max(prev[j], cur[j - 1]) # 不匹配,取上方或左方较大者
prev, cur = cur, prev # 滚动:当前行变上一行,旧上一行复用为草稿
print(prev[m])
if __name__ == "__main__":
solve()复杂度
- 时间复杂度:
, 、 分别为两串长度,均不超过 。 - 空间复杂度:
,仅保留两行长度各为 的整型数组。
总结
最长公共子序列是典型的二维动态规划模型。状态定义以两个前缀为维度,按末字符是否相等分类转移;再利用滚动数组把空间压到一维。实现时注意