news 2026/6/23 1:15:49

动态规划在字符串匹配中的艺术:从编辑距离到正则匹配

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划在字符串匹配中的艺术:从编辑距离到正则匹配

探索动态规划如何优雅地解决复杂的字符串匹配问题,从基础编辑操作到强大的模式匹配引擎

字符串处理是计算机科学的核心问题之一,而动态规划为字符串匹配提供了系统性的解决方案框架。本文将深入探讨几种经典的字符串匹配问题及其动态规划解法,并使用C++实现完整解决方案。

1. 字符串匹配问题的动态规划视角

动态规划解决字符串匹配问题的核心思想是:将复杂匹配问题分解为子问题,利用重叠子问题性质避免重复计算,并通过最优子结构找到最优解

1.1 动态规划在字符串问题中的优势

  • 系统化:提供统一的解题框架

  • 高效性:时间复杂度通常为O(n²)或O(nm)

  • 灵活性:可处理各种约束条件

  • 可解释性:递推关系清晰反映问题本质

2. 编辑距离:字符串相似度度量

编辑距离(Levenshtein距离)衡量两个字符串的相似程度,定义为将一个字符串转换为另一个字符串所需的最少操作次数,允许的操作包括插入、删除、替换。

2.1 问题定义

给定两个字符串word1word2,计算它们的最小编辑距离。

示例:输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将'h'替换为'r')
rorse -> rose (删除第二个'r')
rose -> ros (删除'e')

2.2 动态规划解法

状态定义

定义dp[i][j]表示word1的前i个字符转换为word2的前j个字符所需的最小操作数。

if (word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1]; // 字符相同,无需操作
} else {
dp[i][j] = 1 + min(
dp[i-1][j], // 删除word1[i-1]
dp[i][j-1], // 在word1中插入word2[j-1]
dp[i-1][j-1] // 替换word1[i-1]为word2[j-1]
);
}

完整实现

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class EditDistance {
public:
int minDistance(string word1, string word2) {
int m = word1.length();
int n = word2.length();

// 创建DP表
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

// 初始化边界条件
for (int i = 0; i <= m; i++) {
dp[i][0] = i; // 将word1的前i个字符变为空串
}
for (int j = 0; j <= n; j++) {
dp[0][j] = j; // 将空串变为word2的前j个字符
}

// 状态转移
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = 1 + min({
dp[i-1][j], // 删除
dp[i][j-1], // 插入
dp[i-1][j-1] // 替换
});
}
}
}

return dp[m][n];
}

// 空间优化版本
int minDistanceOptimized(string word1, string word2) {
int m = word1.length();
int n = word2.length();

// 确保word1是较短的字符串,以优化空间
if (m < n) {
swap(word1, word2);
swap(m, n);
}

vector<int> prev(n + 1, 0);
vector<int> curr(n + 1, 0);

// 初始化第一行
for (int j = 0; j <= n; j++) {
prev[j] = j;
}

for (int i = 1; i <= m; i++) {
curr[0] = i; // 对应dp[i][0]
for (int j = 1; j <= n; j++) {
if (word1[i-1] == word2[j-1]) {
curr[j] = prev[j-1];
} else {
curr[j] = 1 + min({
prev[j], // 删除
curr[j-1], // 插入
prev[j-1] // 替换
});
}
}
prev = curr;
}

return prev[n];
}
};

int main() {
EditDistance ed;

vector<pair<string, string>> testCases = {
{"horse", "ros"},
{"intention", "execution"},
{"", "abc"},
{"abc", ""},
{"abc", "abc"}
};

cout << "编辑距离测试:" << endl;
for (auto& test : testCases) {
int result1 = ed.minDistance(test.first, test.second);
int result2 = ed.minDistanceOptimized(test.first, test.second);
cout << "minDistance(\"" << test.first << "\", \"" << test.second
<< "\") = " << result1
<< " (优化版本: " << result2 << ")" << endl;
}

return 0;
}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/21 13:53:52

GO 日志的规范使用

平时不太说废话&#xff0c;今天主要讲讲编程素质类的内容大家有兴趣的酌情阅读 GO语言日志的规范使用 在任何服务端的语言项目中&#xff0c;日志是至关重要的组成部分&#xff0c;它能够记录系统的运行状态、错误信息和关键事件&#xff0c;对于问题排查、性能优化以及系统…

作者头像 李华
网站建设 2026/6/19 10:22:16

Langchain v1.0+ 浅出

Langchain 浅出 原本的计划是发一篇《langchain深入浅出》&#xff0c;但是太长了就切割成了两部份&#xff0c;先发了这篇浅出 中间还经历了大版本的更新╮(╯▽╰)╭但是有惊无险还是写完了。 作者&#xff1a;吴佳浩 最后更新&#xff1a;2025-11-27 适用版本&#xff1a;…

作者头像 李华
网站建设 2026/6/21 15:08:47

一篇拿下!C++:类和对象(中)构造函数与析构函数

第一&#xff1a;我们不写时&#xff0c;编译器默认生成的函数行为是什么&#xff0c;是否满足我们的需求。第二&#xff1a;编译器默认生成的函数不满足我们的需求&#xff0c;我们需要自己实现&#xff0c;那么如何自己实现&#xff1f;二、构造函数构造函数是特殊的成员函数…

作者头像 李华
网站建设 2026/6/19 22:59:57

torch 操作函数

torch.multinomial torch.multinomial 用于从多项分布中采样。给定一个包含概率的张量&#xff0c;该函数会根据这些概率返回采样的索引。适用于需要根据概率分布进行随机选择的场景&#xff0c;例如在强化学习中根据策略选择动作。 输入是一个概率分布张量&#xff0c;每个元素…

作者头像 李华
网站建设 2026/6/22 6:22:41

23、PHP编程与相关技术全解析

PHP编程与相关技术全解析 1. PHP基础操作与数据获取 在PHP编程中,对于日志条目的操作是一个常见场景。首先,会将日志条目的ID号存储在 $id 变量中。接着,会进行条件判断,如果存在ID号且该ID号大于0,程序会执行一系列操作。具体步骤如下: 1. 连接到服务器。 2. 选择…

作者头像 李华
网站建设 2026/6/22 18:51:53

46、使用容器更新和管理 SQL Server:全面指南

使用容器更新和管理 SQL Server:全面指南 1. 使用容器更新 SQL Server 在 RHEL 上更新 Linux 版 SQL Server 到新的累积更新时,通常会运行 sudo yum update mssql-server 命令。此命令会下载最新累积更新,关闭 SQL Server,应用新二进制文件,然后重新启动 SQL Server。…

作者头像 李华