编辑距离是处理文本纠错时常用的一个方法,英文叫Edit Distance
(以下内容参考https://www.geeksforgeeks.org/edit-distance-dp-5/)
给定两个字符串str1和str2,我们可以在str1上执行以下3种操作来讲str1调整成为str2:
- 插入
- 删除
- 替换
例如:
Input: str1 = "geek", str2 = "gesek"
Output: 1
可以在"geek"之间插入 's'.
Input: str1 = "cat", str2 = "cut"
Output: 1
可以将"cat"中的"a"替换为"u".
Input: str1 = "sunday", str2 = "saturday"
Output: 3
由于str1和str2的头第一个字符"s"和尾三个字符"day"是一样的,所以只需要将"un"改编为"stur".
可以将"n"替换为"r",再在前面插入"st".
m: Length of str1 (first string)
n: Length of str2 (second string)
我们对于字符串的操作流程如下:
1、从两个字符串的最后一个字符开始,如果两个字符是一样的则什么也不做,转而比较接下来的m-1和n-1个字符。
2、最后两个字符如果不一样,则可以采用上述三种操作使之一样,使编辑距离+1,然后转而比较接下来的。
- 插入:则比较str1剩下的m个字符和str2剩下的n-1个字符;
- 删除:则比较str1剩下的m-1个字符和str2剩下的n个字符;
- 替换:则比较str1剩下的m-1个字符和str2剩下的n-1个字符;
#c++
// A Naive recursive C++ program to find minimum number
// operations to convert str1 to str2
#include <bits/stdc++.h>
using namespace std;
// Utility function to find minimum of three numbers
int min(int x, int y, int z) {
return min(min(x, y), z);
}
int editDist(string str1, string str2, int m, int n)
{
// If first string is empty, the only option is to
// insert all characters of second string into first
if (m == 0)
return n;
// If second string is empty, the only option is to
// remove all characters of first string
if (n == 0)
return m;
// If last characters of two strings are same, nothing
// much to do. Ignore last characters and get count for
// remaining strings.
if (str1[m - 1] == str2[n - 1])
return editDist(str1, str2, m - 1, n - 1);
// If last characters are not same, consider all three
// operations on last character of first string,
// recursively compute minimum cost for all three
// operations and take minimum of three values.
return 1
+ min(editDist(str1, str2, m, n - 1), // Insert
editDist(str1, str2, m - 1, n), // Remove
editDist(str1, str2, m - 1,
n - 1) // Replace
);
}
int main()
{
// your code goes here
string str1 = "sunday";
string str2 = "saturday";
cout << editDist(str1, str2, str1.length(),
str2.length());
return 0;
}
#python
def editDistance(str1, str2, m, n):
if m == 0:
return n
if n == 0:
return m
if str1[m-1] == str2[n-1]:
return editDistance(str1, str2, m-1, n-1)
return 1 + min(editDistance(str1, str2, m, n-1),
editDistance(str1, str2, m-1, n),
editDistance(str1, str2, m-1, n-1) )
str1 = "sunday"
str2 = "saturday"
print editDistance(str1, str2, len(str1), len(str2))
当然,上述的方法在某些情况下存在大量重复计算,这在很多DP问题中也出现过,解决方法可以在Edit Distance中参考。
代码如下:
# 基于动态规划的解法
def edit_dist(str1, str2):
# m,n分别字符串str1和str2的长度
m, n = len(str1), len(str2)
# 构建二维数组来存储子问题(sub-problem)的答案
dp = [[0 for x in range(n+1)] for x in range(m+1)]
# 利用动态规划算法,填充数组
for i in range(m+1):
for j in range(n+1):
# 假设第一个字符串为空,则转换的代价为j (j次的插入)
if i == 0:
dp[i][j] = j
# 同样的,假设第二个字符串为空,则转换的代价为i (i次的插入)
elif j == 0:
dp[i][j] = i
# 如果最后一个字符相等,就不会产生代价
elif str1[i-1] == str2[j-1]:
dp[i][j] = dp[i-1][j-1]
# 如果最后一个字符不一样,则考虑多种可能性,并且选择其中最小的值
else:
dp[i][j] = 1 + min(dp[i][j-1], # Insert
dp[i-1][j], # Remove
dp[i-1][j-1]) # Replace
return dp[m][n]