文本比较算法--Needleman/Wunsch算法

一、定义:

定义:
LCS(A,B)表示字符串A和字符串B的最长公共子串的长度。

很显然,LCS(A,B)=0 表示两个字符串没有公共部分。

例如,字符串A=kitten,字符串B=sitting ,那他们的最长公共子串为ittn ,最长公共子串长度为4。

(注:最长公共子串不需要连续出现,但一定是出现的顺序一致),


二、公式:

为了讲解计算LCS(A,B),特给予以下几个定义:

A=a1a2……aN,表示A是由a1a2……aN这N个字符组成,Len(A)=N;

B=b1b2……bM,表示B是由b1b2……bM这M个字符组成,Len(B)=M.

定义LCS(i,j)=LCS(a1a2……ai,b1b2……bj),其中0≤i≤N,0≤j≤M.

对于1≤i≤N,1≤j≤M,有公式:

若ai=bj,则LCS(i,j)=LCS(i-1,j-1)+1
若ai≠bj,则LCS(i,j)=Max(LCS(i-1,j-1),LCS(i-1,j),LCS(i,j-1))

计算LCS(A,B)的算法有很多,下面介绍的Needleman/Wunsch算法是其中的一种。和LD算法类似,Needleman/Wunsch算法用的都是动态规划的思想。


三、举例说明:

举例说明:A=GGATCGA,B=GAATTCAGTTA,计算LCS(A,B)

第一步:初始化LCS矩阵

文本比较算法--Needleman/Wunsch算法_第1张图片

第二步:利用公式,计算矩阵的第一行

文本比较算法--Needleman/Wunsch算法_第2张图片

第三步:利用公式,计算矩阵的其余各行

文本比较算法--Needleman/Wunsch算法_第3张图片

则,LCS(A,B)=6.

可以看出,Needleman/Wunsch算法实际上和LD算法是非常接近的。故他们的时间复杂度和空间复杂度也一样。时间复杂度为O(MN),空间复杂度为O(MN)。空间复杂度经过优化,可以优化到O(M),但是一旦优化就丧失了计算匹配字串的机会了。



还是以上面为例A=GGATCGA,B=GAATTCAGTTA,LCS(A,B)=6.

他们的匹配为:
文本比较算法--Needleman/Wunsch算法_第4张图片
  
如上面所示,蓝色表示完全匹配,黑色表示编辑操作,_表示插入字符或者是删除字符操作。如上面所示,蓝色字符有6个,表示最长公共子串长度为6。


利用上面的Needleman/Wunsch算法矩阵,通过回溯,能找到匹配字串


第一步:定位在矩阵的右下角

文本比较算法--Needleman/Wunsch算法_第5张图片


第二步:回溯单元格,至矩阵的左上角

若ai=bj,则回溯到左上角单元格

文本比较算法--Needleman/Wunsch算法_第6张图片

若ai≠bj,回溯到左上角、上边、左边中值最大的单元格,若有相同最大值的单元格,优先级按照左上角、上边、左边的顺序

文本比较算法--Needleman/Wunsch算法_第7张图片

若当前单元格是在矩阵的第一行,则回溯至左边的单元格
若当前单元格是在矩阵的第一列,则回溯至上边的单元格

文本比较算法--Needleman/Wunsch算法_第8张图片
依照上面的回溯法则,回溯到矩阵的左上角


第三步:根据回溯路径,写出匹配字串

若回溯到左上角单元格,将ai添加到匹配字串A,将bj添加到匹配字串B
若回溯到上边单元格,将ai添加到匹配字串A,将_添加到匹配字串B
若回溯到左边单元格,将_添加到匹配字串A,将bj添加到匹配字串B
搜索晚整个匹配路径,匹配字串也就完成了


四、代码案例

见博主文章:腾讯笔试编程题:构造回文(C++)

你可能感兴趣的:(算法,算法,最大公共子串,动态规划)