末位三选一的前缀 DP:相同取对角线,不同取删除/插入/替换三个相邻状态的 min 加一,再用滚动一维数组把空间压到 O(m)。
OJ: roj
题目 ID: 1276
难度:普及-
标签:字符串动态规划线性DPpython
创建: 2026-09-30 02:55
更新: 2026-10-05 07:44
题目描述
给定字符串 sfdqxbw 与 gfdgw,输出 4。
思路
设
参考代码
Python 版:
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-09-30 02:53
# update_at: 2026-09-30 02:53
import sys
def edit_distance(a: str, b: str) -> int:
"""把 a 变成 b 的最少操作数:滚动一维 DP,prev 恒指"上一行"。"""
prev = list(range(len(b) + 1)) # a 空前缀:b[0..j-1] 只能逐个插入
for i, ca in enumerate(a, 1):
cur = [i] + [0] * len(b) # b 空前缀:a[0..i-1] 只能逐个删除
for j, cb in enumerate(b, 1):
# 收尾三种选择:删掉 ca、插入 cb、把 ca 对上 cb(相同则零代价)
cur[j] = prev[j - 1] if ca == cb else 1 + min(prev[j], cur[j - 1], prev[j - 1])
prev = cur
return prev[-1]
def solve() -> None:
data = iter(sys.stdin.buffer.read().split())
a, b = next(data).decode(), next(data).decode()
print(edit_distance(a, b))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* 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-10-05 07:44
* update_at: 2026-10-05 07:44
*/
// main.cpp:编辑距离,滚动一维数组的前缀 DP。
#include <iostream>
#include <cstring>
#include <algorithm>
typedef long long ll;
const int MAXLEN = 2005;
char a[MAXLEN]; // 字符串 A,下标从 1 开始使用
char b[MAXLEN]; // 字符串 B,下标从 1 开始使用
int dp_prev[MAXLEN]; // dp_prev[j]:上一行(A 的前 i-1 个字符)变成 B 前 j 个字符的最少次数
int dp_cur[MAXLEN]; // dp_cur[j] :当前行(A 的前 i 个字符)变成 B 前 j 个字符的最少次数
int main() {
std::cin >> (a + 1) >> (b + 1);
ll n = strlen(a + 1);
ll m = strlen(b + 1);
// 边界:A 为空前缀,B 的前 j 个字符只能逐个插入
for (ll j = 0; j <= m; j++) {
dp_prev[j] = j;
}
for (ll i = 1; i <= n; i++) {
dp_cur[0] = i; // 边界:B 为空前缀,A 的前 i 个字符只能逐个删除
for (ll j = 1; j <= m; j++) {
if (a[i] == b[j]) {
// 末位字符相同,直接对上,零代价
dp_cur[j] = dp_prev[j - 1];
} else {
// 三种收尾取最小:删 a[i] / 插 b[j] / 改写 a[i]
int best = std::min(dp_prev[j], dp_cur[j - 1]);
best = std::min(best, dp_prev[j - 1]);
dp_cur[j] = best + 1;
}
}
// 当前行变成下一轮的上一行
for (ll j = 0; j <= m; j++) {
dp_prev[j] = dp_cur[j];
}
}
std::cout << dp_prev[m] << std::endl;
return 0;
}