Levenshtein distance
The Levenshtein distance is a metric for measuring the amount of difference between two sequences (i.e. an edit distance). The term edit distance is often used to refer specifically to Levenshtein distance.The Levenshtein distance between two strings is defined as the minimum number of edits needed to transform one string into the other, with the allowable edit operations being insertion, deletion, or substitution of a single character. It is named afterVladimir Levenshtein, who considered this distance in 1965.[1]
ExampleFor example, the Levenshtein distance between "kitten" and "sitting" is 3, since the following three edits change one into the other, and there is no way to do it with fewer than three edits:
- kitten → sitten (substitution of 's' for 'k')
- sitten → sittin (substitution of 'i' for 'e')
- sittin → sitting (insertion of 'g' at the end).
Computing Levenshtein distance
Computing the Levenshtein distance is based on the observation that if we reserve amatrix to hold the Levenshtein distances between all prefixes of the first string and all prefixes of the second, then we can compute the values in the matrix byflood filling the matrix, and thus find the distance between the two full strings as the last value computed.
This algorithm, an example of bottom-up dynamic programming, is discussed, with variants, in the 1974 article TheString-to-string correction problem by Robert A. Wagner and Michael J. Fischer.
A straightforward implementation, as pseudocode for a function LevenshteinDistance that takes two strings,s of length m, and t of length n, and returns the Levenshtein distance between them:
int LevenshteinDistance(char s[1..m], char t[1..n])
{
// for all i and j, d[i,j] will hold the Levenshtein distance between
// the first i characters of s and the first j characters of t;
// note that d has (m+1)x(n+1) values
declare int d[0..m, 0..n]
for i from 0 to m
d[i, 0] := i // the distance of any first string to an empty second string
for j from 0 to n
d[0, j] := j // the distance of any second string to an empty first string
for j from 1 to n
{
for i from 1 to m
{
if s[i] = t[j] then
d[i, j] := d[i-1, j-1] // no operation required
else
d[i, j] := minimum
(
d[i-1, j] + 1, // a deletion
d[i, j-1] + 1, // an insertion
d[i-1, j-1] + 1 // a substitution
)
}
}
return d[m,n]
}
Two examples of the resulting matrix (hovering over a number reveals the operation performed to get that number):
The invariant maintained throughout the algorithm is that we can transform the initial segments[1..i]
into t[1..j]
using a minimum of d[i,j]
operations. At the end, the bottom-right element of the array contains the answer.
Proof of correctness
As mentioned earlier, the invariant is that we can transform the initial segment s[1..i]
intot[1..j]
using a minimum of d[i,j]
operations. This invariant holds since:
- It is initially true on row and column 0 because
s[1..i]
can be transformed into the empty stringt[1..0]
by simply dropping alli
characters. Similarly, we can transforms[1..0]
tot[1..j]
by simply adding allj
characters. - If
s[i] = t[j]
, and we can transforms[1..i-1]
tot[1..j-1]
ink
operations, then we can do the same tos[1..i]
and just leave the last character alone, givingk
operations. - Otherwise, the distance is the minimum of the three possible ways to do the transformation:
- If we can transform
s[1..i]
tot[1..j-1]
ink
operations, then we can simply addt[j]
afterwards to gett[1..j]
ink+1
operations (insertion). - If we can transform
s[1..i-1]
tot[1..j]
ink
operations, then we can removes[i]
and then do the same transformation, for a total ofk+1
operations (deletion). - If we can transform
s[1..i-1]
tot[1..j-1]
ink
operations, then we can do the same tos[1..i]
, and exchange the originals[i]
fort[j]
afterwards, for a total ofk+1
operations (substitution).
- If we can transform
- The operations required to transform
s[1..n]
intot[1..m]
is of course the number required to transform all ofs
into all oft
, and sod[n,m]
holds our result.
This proof fails to validate that the number placed in d[i,j]
is in fact minimal; this is more difficult to show, and involves anargument by contradiction in which we assume d[i,j]
is smaller than the minimum of the three, and use this to show one of the three is not minimal.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//算法
int ldistance(const string source,const string target)
{
//step 1
int n = source.length();
int m = target.length();
if (m == 0) return n;
if (n == 0) return m;
//Construct a matrix
typedef vector< vector<int> > Tmatrix;
Tmatrix matrix(n+1);
for(int i=0; i<=n; i++) matrix[i].resize(m+1);
//step 2 Initialize
for(int i=1;i<=n;i++) matrix[i][0]=i;
for(int i=1;i<=m;i++) matrix[0][i]=i;
//step 3
for(int i=1;i<=n;i++)
{
//step 4
for(int j=1;j<=m;j++)
{
//step 5
if(source[i-1] == target[j-1]){
matrix[i][j] = matrix[i-1][j-1];
}
else{
matrix[i][j] = min(matrix[i-1][j]+1,min(matrix[i][j-1]+1,matrix[i-1][j-1]+1));
}
}
}//step7
return matrix[n][m];
}
int main(){
string s;
string d;
while (1)
{
cout<<"first string =";
cin>>s;
cout<<"second string =";
cin>>d;
int dist=ldistance(s,d);
cout<<"dist="<<dist<<endl;
}
}