Edit Distance
编辑距离是针对二个字符串(例如英文字)的差异程度的量化量测,量测方式是看至少需要多少次的处理才能将一個字符串变成另一個字符串。
事实上编辑距离有几种不同的定义:
1. Levenshtein (莱文斯坦距离) ----通常的定义,编辑操作包括可以删除、插入、替换字符串中的任意一个字元。
2. Damerau-Levenshtein → 1 的 变种,还可以允许一单一操作交换相邻的两个字符 (转置)。即 aba → aab 的编辑距离为 1 而非 Levenshtein 的 2 。
3. LCS - D(详见后续的最长公共子序列问题) 只允许删除和插入。
4. Hamming Distance (汉明距离) 只允许替换。
5. Jaro 只允许字符转置。
Problem
给定 2 个字符串
. 编辑距离是将
的最少操作次数,操作只允许如下 3 种:
插入一个字符,例如:ha → hat
删除一个字符,例如:abc → ac
替换一个字符,例如:sex → hex
Analyze
- 编辑距离是对称的,即 等于
- 令 = .length(), = .length()
- 两个字符串的编辑距离不会超过 max{ , }
- 两个字符串的编辑距离一定不小于 abs( - )
Solution
首先将问题分解为若干个彼此接近的小问题。
假设 dp[i,j] 表示我们当前长度为 i 的 str 0 的前缀 和 长度为j的 str1 的前缀的编辑距离,不难发现 dp[i,j] 根据三种操作分类有关。
- 若是 i = 0, 也就是需要插入j个元素 故 dp[ 0,j ] = j;
- 若是 j= 0, 也就是需要插入j个元素 故 dp[ i,0 ] = i;
-
,此时根据当前位的字符比较可得出以下的递推关系
Code
void toString(int* arr,int l1,int l2)
{
for ( int i = 0; i < l1; ++i)
{
for( int j = 0; j < l2; ++j)
{
if(j != l2 -1) std::cout << *(arr + i*l2 + j) <<" ";
else std::cout << *(arr + i*l2 + j);
}
std::cout << std::endl;
}
}
void init(int* arr,int l1,int l2)
{
*arr = 0;
for (int i = 1; i < l1; ++i) {
*(arr + i*l2) = i;
}
for (int i = 1; i < l2; ++i) {
*(arr + i) = i;
}
}
int calc_edit_distanace(std::string s1, std::string s2) {
int edit_distance;
int dp[s1.length()+1][s2.length()+1];
init(*dp,s1.length()+1,s2.length()+1);
for (int i = 1; i <= s1.length(); ++i) {
for (int j = 1; j <= s2.length(); ++j) {
if(s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i-1][j-1];
else {
int m = dp[i-1][j-1]+1;
dp[i][j] = std::min(std::min(m,dp[i-1][j]+1),std::min(m,dp[i][j-1]+1));
}
}
}
//toString(*dp,s1.length()+1,s2.length()+1);
edit_distance = dp[s1.length()][s2.length()];
return edit_distance;
}
/*
* ‘ Wagener Ficher ’ Algorithm @end.
*/
时间复杂度和空间复杂度均为 O( l1 * l2 );
Next 进行优化.