LCS算法的两种JAVA实现方式

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();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值