文本比较的核心就是比较两个给定的文本(可以是字节流等)之间的差异。目前,主流的比较文本之间的差异主要有两大类。一类是基于编辑距离(Edit Distance)的,例如LD算法。一类是基于最长公共子串的(Longest Common Subsequence),例如Needleman/Wunsch算法等。
LD算法(Levenshtein Distance)又成为编辑距离算法(Edit Distance)。他是以字符串A通过插入字符、删除字符、替换字符变成另一个字符串B,那么操作的过程的次数表示两个字符串的差异。
例如:字符串A:kitten如何变成字符串B:sitting。
第一步:kitten——》sitten。k替换成s
第二步:sitten——》sittin。e替换成i
第三步:sittin——》sitting。在末尾插入g
故kitten和sitting的编辑距离为3
定义说明:
LD(A,B)表示字符串A和字符串B的编辑距离。很显然,若LD(A,B)=0表示字符串A和字符串B完全相同
Rev(A)表示反转字符串A
Len(A)表示字符串A的长度
A+B表示连接字符串A和字符串B
有下面几个性质:
LD(A,A)=0
LD(A,"")=Len(A)
LD(A,B)=LD(B,A)
0≤LD(A,B)≤Max(Len(A),Len(B))
LD(A,B)=LD(Rev(A),Rev(B))
LD(A+C,B+C)=LD(A,B)
LD(A+B,A+C)=LD(B,C)
LD(A,B)≤LD(A,C)+LD(B,C)(注:像不像“三角形,两边之和大于第三边”)
LD(A+C,B)≤LD(A,B)+LD(B,C)
为了讲解计算LD(A,B),特给予以下几个定义
A=a1a2……aN,表示A是由a1a2……aN这N个字符组成,Len(A)=N
B=b1b2……bM,表示B是由b1b2……bM这M个字符组成,Len(B)=M
定义LD(i,j)=LD(a1a2……ai,b1b2……bj),其中0≤i≤N,0≤j≤M
故: LD(N,M)=LD(A,B)
LD(0,0)=0
LD(0,j)=j
LD(i,0)=i
对于1≤i≤N,1≤j≤M,有公式一
若ai=bj,则LD(i,j)=LD(i-1,j-1)
若ai≠bj,则LD(i,j)=Min(LD(i-1,j-1),LD(i-1,j),LD(i,j-1))+1
举例说明:A=GGATCGA,B=GAATTCAGTTA,计算LD(A,B)
第一步:初始化LD矩阵
G | A | A | T | T | C | A | G | T | T | A | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
G | 1 | |||||||||||
G | 2 | |||||||||||
A | 3 | |||||||||||
T | 4 | |||||||||||
C | 5 | |||||||||||
G | 6 | |||||||||||
A | 7 |
第二步:利用上述的公式一,计算第一行
G | A | A | T | T | C | A | G | T | T | A | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
G | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
G | 2 | |||||||||||
A | 3 | |||||||||||
T | 4 | |||||||||||
C | 5 | |||||||||||
G | 6 | |||||||||||
A | 7 |
第三步,利用上述的公示一,计算其余各行
G | A | A | T | T | C | A | G | T | T | A | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
G | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
G | 2 | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 6 | 7 | 8 | 9 |
A | 3 | 2 | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 8 |
T | 4 | 3 | 2 | 2 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
C | 5 | 4 | 3 | 3 | 2 | 2 | 2 | 3 | 4 | 5 | 6 | 7 |
G | 6 | 5 | 4 | 4 | 3 | 3 | 3 | 3 | 3 | 4 | 5 | 6 |
A | 7 | 6 | 5 | 4 | 4 | 4 | 4 | 3 | 4 | 4 | 5 | 5 |
则LD(A,B)=LD(7,11)=5
c++代码如下:
#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++) { const char si=source[i-1]; //step 4 for(int j=1;j<=m;j++) { const char dj=target[j-1]; //step 5 int cost; if(si==dj){ cost=0; } else{ cost=1; } //step 6 const int above=matrix[i-1][j]+1; const int left=matrix[i][j-1]+1; const int diag=matrix[i-1][j-1]+cost; matrix[i][j]=min(above,min(left,diag)); } }//step7 return matrix[n][m]; } int main(){ string s; string d; cout<<"source="; cin>>s; cout<<"diag="; cin>>d; int dist=ldistance(s,d); cout<<"dist="<<dist<<endl; }
LD算法的时间和空间复杂度是O(mn) n m 是两个字符串的长度,对于大型字符串空间复杂度能够优化到O(n)时间复杂度只能是O(n*n)
对于空间复杂度的优化程序如下:
还没有释放内存,使用的时候得对2围数组delete
#include <iostream>
#include <string>
using namespace std;
void str_swp(string &str1, string &str2)
{
string tmp = str1;
str1 = str2;
str2 = tmp;
}
int ld_distance(string str1, string str2)
{
int str1_len = str1.size();
int str2_len = str2.size();
int min_len;
int cost = 0;
if(str2_len == 0)
{
return str1_len;
}
if(str1_len == 0)
{
return str2_len;
}
// len is min
// str1 min str2
if(str1_len < str2_len)
{
min_len = str1_len;
}
else
{
str_swp(str1, str2);
min_len = str2_len;
}
str1_len = str1.size();
str2_len = str2.size();
int **ld_arr = new int*[2];
ld_arr[0] = new int[min_len+1];
ld_arr[1] = new int[min_len+1];
for(int i = 0;i <= min_len; i++)
{
ld_arr[0][i] = i;
}
ld_arr[1][0] = 1;
for(int i = 1; i <= str2_len; i++)
{
for(int j = 1; j <= str1_len; j++)
{
if(str1[j-1] == str2[i-1])
{
cost = 0;
}
else
{
cost = 1;
}
ld_arr[1][j] = min((ld_arr[0][j-1]+cost), min(ld_arr[0][j],ld_arr[1][j-1]));
}
for(int p=0;p <= min_len;p++)
{
ld_arr[0][p] = ld_arr[1][p];
}
ld_arr[0][1] = ld_arr[0][0]+1;
}
return ld_arr[1][min_len];
}
int main()
{
string a="xinxin1";
string b="xinxin";
int ret = ld_distance(a,b);
cout<<"ld_distance is"<<ret<<endl;
return 0;
}