您的位置:首页 > 编程语言 > Java开发

LCS算法的两种JAVA实现方式

2015-06-14 12:39 525 查看
http://blog.csdn.net/dylgsy/article/details/8235778

http://blog.csdn.net/v_july_v/article/details/6695482

给定字符串A,B

Solution  I:

1.构造数组 c i  j 描述A串的前i位和B串的前J位的LCS长度

2.构造数组 trace  i  j  描述max相应位置得到的长度是由哪一步得出的

c的构造方法根据下列规则



trace的构造如下图所示



END和START表示该处是直接得到的结果,没有前置步骤。

END表示LCS=0,START表示LCS=1

其余的根据箭头方向来看就比较好理解了。

获得结果时从上往下遍历即可,总的时间复杂度O(mn)+O(m+n)

代码如下

//枚举
enum DIRECTION{
<span style="white-space:pre">		</span>LEFT,UP,UPLEFT,START,END
<span style="white-space:pre">	</span>}
//核心算法
...
for(int i=0;i<ch1.length;i++)
for(int j=0;j<ch2.length;j++){
if(ch1[i]!=ch2[j]){
if(i>0&&j>0){
if(c[i-1][j]>c[i][j-1]){
trace[i][j]=DIRECTION.LEFT;
c[i][j]=c[i-1][j];
}
else{
trace[i][j]=DIRECTION.UP;
c[i][j]=c[i][j-1];
}
}else{
if(i>0){
trace[i][j]=DIRECTION.LEFT;
c[i][j]=c[i-1][j];
}else if(j>0){
trace[i][j]=DIRECTION.UP;
c[i][j]=c[i][j-1];
}else{
trace[i][j]=DIRECTION.END;
c[i][j]=0;
}
}
}else{
if(i==0||j==0){
trace[i][j]=DIRECTION.START;
c[i][j]=1;
}
else{
trace[i][j]=DIRECTION.UPLEFT;
c[i][j]+=c[i-1][j-1]+1;
}
}
}
//calculate
StringBuilder sb=new StringBuilder("");
int i=ch1.length-1,j=ch2.length-1;
end:
for(;i>=0&&j>=0;)
switch(trace[i][j]){
case UP:j--;break;
case LEFT:i--;break;
case UPLEFT:sb.append(ch1[i]);j--;i--;break;
case START:sb.append(ch1[i]);break end;
case END:break end;
}
//输出(sb.reverse().toString());
//输出(c[ch1.length-1][ch2.length-1]);


Solution II:

构造矩阵L(p,m)

算法实现方式不复杂,原理并不是很明了,这里把过程详细描述一下,希望可以得到一些讨论。

首先假设A串长度不超过B串。

构造基于A串长度的方阵L(i,k)表示A[1..i]中长度为k的子串在B中最小位置(这里认为字符串的位置是最末尾字符的位置),如果不存在这样的子串,则长度为无穷(MAX)。

显然,LCS={k|L(A.length,k)!=MAX}max。

过程也是从下至上递推的。

首先初始化L(1,i),注意L(i,k)=k时,L(i,k+1),L(i,k+2)....均为k,因为相同长度的子串,位置最小的一定是从头开始的那一个。

然后循环,遍历并生成L,规则如下:

i>k时,显然,L(i,k)=MAX

如果L(i-1,k-1)存在,则

从L(i-1,k-1)+1开始遍历到B串结尾,遍历的位置记为j,若B[j]=A[i],则:

比较L(i-1,k)(如果存在)和J的大小,较小的一个即为所求的L(i,k)

这里因为上一步所求的结果中,L(i-1.k-1)实际上可以看做L(i,k)的一部分,因为如果前者(长度为k-1的LCS)存在的话,在L(i-1.k-1)所得的位置往后遍历,遇到与A[i]相等的字符,一定可以与前者组成长度为k的LCS。

L(i,k)=k时,L(i,k+1),L(i,k+2)....均为k,原因同上。

对于一个k,遍历i一遍后,所有L(i,k)均不存在,则k-1,即最后一次所得的k,即为我们要的LCS长度。

B[L(1,m-p+1)]B[L(2,m-p+2)]…B[L(k,i)]即为LCS。

代码如下:

int result=0;
//Li(k)
int[][] L=new int[char1.length+1][char1.length+1];
//init L[k][i] k=1
for(int i=1;i<=char1.length;i++){
L[1][i]=1+str2.indexOf(String.valueOf(char1[i-1]));
if(L[1][i]==1){
for(int j=i+1;j<=char1.length;j++)
L[1][j]=1;
break;
}
}
int nullCount=0;
for(int k=2;k<=char1.length;k++){
nullCount=0;
for(int i=1;i<=char1.length;i++){
if(i<k){
L[k][i]=MAX;
nullCount++;
continue;
}
int min=MAX;
if(L[k-1][i-1]!=MAX){
if(L[k][i-1]!=MAX)
min=L[k][i-1];
for(int j=L[k-1][i-1]+1;j<=char2.length;j++)
if(char2[j-1]==char1[i-1]){
min=j<min?j:min;
break;
}
}
L[k][i]=min;
if(L[k][i]==k){
for(int j=i+1;j<=char1.length;j++)
L[k][j]=k;
break;
}
if(L[k][i]==MAX)
nullCount++;
}
if(nullCount==char1.length){
result=k-1;
break;
}
}
result=result==0?char1.length:result;
StringBuilder sb=new StringBuilder();
int i=char1.length,k=result;
while(k>0&&i>0)
sb.append(char2[L[k--][i--]-1]);
sb.reverse();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: