本人近日阅读了《编程之美》,参阅了其中的——计算字符串的相似度——一节。感觉颇为实用。现将这一文章贴于此处,并将代码赋予其后。
许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程度。我们定义了一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
1.修改一个字符(如把“a”替换为“b”)。
2.增加一个字符(如把“abdd”变为“aebdd”)。
3.删除一个字符(如把“travelling”变为“traveling”)。
比如,对于“abcdefg”和“abcdef”两个字符串来说,我们认为可以通过增加/减少一个“g“的方式来达到目的。上面的两种方案,都仅需要一次操作。把这个操作所需要的次数定
义为两个字符串的距离,给定任意两个字符串,你是否能写出一个算法来计算出它们的距离?
分析与解法
不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。
我们还是应该集中考虑如何才能把这个问题转化成规模较小的同样的问题。如果有两个串A=xabcdae和B=xfdfa,它们的第一个字符是相同的,只要计算A[2,…,7]=abcdae和B[2,5]=fdfa的距离就可以了。但是如果两个串的第一个字符不相同,那么可以进行如下的操作(lenA和lenB分别是A串和B串的长度):
1.删除A串的第一个字符,然后计算A[2,lenA]和B[1,lenB]的距离。
2.删除B串的第一个字符,然后计算A[1,lenA]和B[2,lenB]的距离。
3.修改A串的第一个字符为B串的第一个字符,然后计算A[2,lenB]的距离。
4.修改B串的第一个字符为A串的第一个字符,然后计算A[2,lenB]的距离。
5.增加B串的第一个字符到A串的第一个字符之前,然后计算A[1,lenB]的距离。
6.增加A串的第一个字符到B串的第一个字符之前,然后计算A[2,lenB]的距离。
在这个题目中,我们并不在乎两个字符串变得相等之后的字符串是怎样的。所以,可以将上面6个操作合并为:
1.一步操作之后,再将A[2,lenB]变成相同字符串。
2.一步操作之后,再将A[1,lenB]变成相同字符串。
3.一步操作之后,再将A[2,lenB]变成相同字符串。
这样,很快就可以完成一个递归程序。
以上摘自原书,有兴趣的读者可以参看原书。下面的代码是根据这个思想实现的。用的是VB2005
Public Class clsCalculateStringDistance
Implements IDistance
Private mStringA As String
Private mStringB As String
Private mIsSame As Boolean
Private mDic As Dictionary(Of String,Integer)
Private Function CalculateStringDistance( _
ByVal StartA As Integer,_
ByVal StartB As Integer) As Integer
Dim tD As Integer
If mDic.ContainsKey(StartA & "," & StartB) = True Then _
Return mDic(StartA & "," & StartB)
If StartA > mStringA.Length Then
If StartB > mStringB.Length Then
Return 0
Else
Return mStringB.Length - StartB + 1
End If
End If
If StartB > mStringB.Length Then
If StartA > mStringA.Length Then
Return 0
Else
Return mStringA.Length - StartA + 1
End If
End If
If mStringA.Chars(StartA - 1) = mStringB.Chars(StartB - 1) Then
tD = CalculateStringDistance(StartA + 1,StartB + 1)
If mIsSame = False Then mDic.Add(StartA & "," & StartB,tD)
Return tD
Else
mIsSame = False
Dim t1 As Integer,t2 As Integer,t3 As Integer
t1 = CalculateStringDistance(StartA,StartB + 1)
t2 = CalculateStringDistance(StartA + 1,StartB)
t3 = CalculateStringDistance(StartA + 1,StartB + 1)
tD = Min(t1,t2,t3) + 1
mDic.Add(StartA & ",tD)
Return tD
End If
End Function
Private Function Min(ByVal ParamArray M() As Integer) As Integer
Dim i As Integer,J As Integer
J = M(0)
For i = 1 To M.GetUpperBound(0)
If M(i) < J Then J = M(i)
Next
Return J
End Function
Public Function CalculateStringDistance() As IntegerImplements
IDistance.CalculateStringDistance
If mStringA.Length = 0 Then Return mStringB.Length
If mStringB.Length = 0 Then Return mStringA.Length
mDic = New Dictionary(Of String,Integer)
mIsSame = True
Return CalculateStringDistance(1,1)
End Function
Public ReadOnly Property DicCount() As IntegerImplements
IDistance.DicCount
Get
Return mDic.Count
End Get
End Property
Public Sub SetString(ByVal S1 As String,ByVal S2 As String) Implements
IDistance.SetString
mStringA = S1
mStringB = S2
End Sub
End Class
本系列的后一篇文章“计算字符串的相似度(VB2005)——思索之二”