您的位置:首页 > 其它

最长公共子序列问题

2010-12-06 16:11 67 查看
 

这是自动判题系统的描述
描述:
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=<x1, x2,…,
xm>,则另一序列Z=<z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列 <i1, i2,…,
ik>,使得对于所有j=1,2,…,k有:

Xij =
Zj

如果一个序列S即是A的子序列又是B的子序列,则称S是A、B的公共子序列。
求A、B所有公共子序列中最长的序列的长度。
输入:
输入共两行,每行一个由字母和数字组成的字符串,代表序列A、B。A、B的长度不超过200个字符。
输出:
一个整数,表示最长各个子序列的长度。
格式:printf("%d/n");
输入样例:
programming
contest
输出样例:
2
 

 
代码:
 
import java.util.Scanner;
public class Main {

public static void main(String[] args) {
Scanner sc=new Scanner(System.in);

String str1=sc.nextLine();
String str2	=sc.nextLine();
System.out.println(lcsLength(str1,str2));

}
//关于此问题,需要递归公式,c[i][j]记录序列Xi和yj的最长
//公共子序列的长度,当i=0或j=0时,空序列是两个字符串的最长
//公共子序列,故此时c[i][j]=0
//当Xi=yi时,c[i][j]=c[i-1][j-1]+1;
//else max{c[i][j-1],c[i-1][j]}
public static int lcsLength(String str1,String str2)
{
int m=str1.length();
int n=str2.length();
int c[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++)
c[i][0]=0;
for(int i=1;i<=n;i++)
c[0][i]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(str1.charAt(i-1)==str2.charAt(j-1))
c[i][j]=c[i-1][j-1]+1;
else if(c[i-1][j]>=c[i][j-1])
c[i][j]=c[i-1][j];
else
c[i][j]=c[i][j-1];

}
return c[m]
;
}
}
 
 
但是这个程序只能计算出最长公共子序列的长度,那要是还想要知道是什么序列该怎么办呢?
import java.util.Scanner;
public class Main {

static int b[][];//此数组用于计算最长公共子序列中每个字符时怎么样得到的

public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String str1=sc.nextLine();
String str2	=sc.nextLine();
b=new int[str1.length()+1][str2.length()+1];

System.out.println(lcsLength(str1,str2,b));
lcs(str1.length(),str2.length(),str1,b);

}
//关于此问题,需要递归公式,c[i][j]记录序列Xi和yj的最长
//公共子序列的长度,当i=0或j=0时,空序列是两个字符串的最长
//公共子序列,故此时c[i][j]=0
//当Xi=yi时,c[i][j]=c[i-1][j-1]+1;
//else max{c[i][j-1],c[i-1][j]}
public static int lcsLength(String str1,String str2,int [][]b)
{
int m=str1.length();
int n=str2.length();
int c[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++)
c[i][0]=0;
for(int i=1;i<=n;i++)
c[0][i]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(str1.charAt(i-1)==str2.charAt(j-1))
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else if(c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else
{
c[i][j]=c[i][j-1];
b[i][j]=3;
}

}
return c[m]
;
}
//构造最长公共子序列
public static void lcs(int i,int j,String str,int b[][])
{
if(i==0||j==0)
return;
if(b[i][j]==1)
{
lcs(i-1,j-1,str,b);
System.out.print(str.charAt(i-1));
}
else if(b[i][j]==2)
lcs(i-1,j,str,b);
else
lcs(i,j-1,str,b);

}
}
 
这个算法可以解决刚才说的问题。
下面由我来进一步解释一下最长公共子序列问题的解法,当然,这不是我发明的,从书上学来的。
最长公共子序列问题具有最优子结构性质。
设序列X={x1,x2,...,xm},Y={y1,y2,...,yn}的最长公共子序列是Z={z1,z2,...zk},则
(1)如果xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列
(2)else if(zk不等于xm),则Z是Xm-1和Y的最长公共子序列
(3)else if(zk不等于yn),则Z是X和Yn-1的最长公共子序列
由此德奥递推公式

公共子序列,故此时c[i][j]=0  i=0 or j=0
当Xi=yi时,c[i][j]=c[i-1][j-1]+1;
else max{c[i][j-1],c[i-1][j]}

 
用数组b[i][j]记录c[i][j]的值是由哪一个子问题的解得到的,在构造最长公共子序列时用到了。
在构造最长公共子序列时,首先从b[m]
开始,依其值在数组b中搜索,当b[i][j]=1,表示Xi和Yj的最长公共子序列是由Xi-1和Yj-1的最长公共子序列尾部加上xi得到的序列。当b[i][j]=2时,表示Xi和Yj的最长公共子序列是由Xi-1和Yj的最长公共子序列相同,当b[i][j]=3时,表示Xi和Yj的最长公共子序列是由Xi和Yj-1的最长公共子序列相同,由此可找出最长公共子序列的具体内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  string c zk import class 算法