版权声明:转载请注明出处,个人创作不易 https://blog.csdn.net/qq_39123517/article/details/87918194
参考 俞征武《图解算法》第三章动态规划中求解最长相同字符串问题。
动态规划的精神:利用小问题的最优解组成大问题的最优解。
动态规划解决问题的3个步骤应该是:
(1)此问题的“大问题的最优解”可以利用“小问题”的最优解求取。
(2)利用一个数学式子将“大问题的最优解”和“小问题的最优解”之间的关系清楚地表达出来。
(3)先将“小问题的最优解”计算出后存储下来,再利用他们计算出“大问题的最优解”。
问题
问题 | 老王找到失散多年的兄弟。为确定两人的缘关系,他决定将两人的基因进行对比,请编写一个程序比较两组基因,并找到这两组基因(字符串)共同拥有的基因组(最长相同子字符串)。注意存在这两组基因的子字符串,其先后顺序需一致。 |
输入 | 两组字符串X=< X=<A,T,C,T,G,A,T> |
输出 | 最长相同子字符串Z=<
Z=<T,C,T,A>, 4 |
算法程序
package 第八章_动态规划;
import java.util.Vector;
/*
* 计算最长公共子序列长度:
* dp[i][j] = 0 i=0或j=0——边界条件
* dp[i][j] = dp[i-1][j-1]+1 a[i-1]=b[j-1]
* dp[i][j] = MAX(dp[i][j-1],dp[i-1][j]) a[i-1]!=b[j-1]
*
* 求最长公共子序列subs(利用vector容器):
* (1)当元素值等于上方相邻元素值(dp[iJ[j]=dp[i-1][j])时i减1。
* (2)否则,当元素值等于左方相邻元素值(dp[i][i]=dp[i][j-1])时j减1.
* (3)若元素值与上方、左边的元素值均不相等(dp[i][j]≠dp[i-1] 且 dp[i][i]≠dp[i][j-1]),
* 说明一定有dp[i][j]=dp[i-1][j-1]+1,此时a[i-1]=b[j-1],将a[i-1]添加到subs中,并将i、j均减1。
*/
public class 最长公共子序列 {
static int MAX = 51; //序列中最多的字符个数
//问题表示
static int m; //a,b序列的长度
static int n;
static String a,b;
//求解结果表示
static int dp[][] = new int[MAX][MAX]; //动态规划数组
static Vector<String> subs= new Vector<String>(); //存放LCS(Longest Common Subsequence)最长公共子序列
public static void LCSlength(char[] A, char[] B) { //求dp
int i,j;
for(i=0; i<=m; i++) //将dp[i][0]置为0,边界条件
dp[i][0] = 0;
for(j=0; j<n; j++) //将dp[0][j]置为0,边界条件
dp[0][j] = 0;
for(i=1; i<=m; i++) //两重for循环处理a、b的所有字符
for(j=1; j<=n; j++) { //情况(1)
if(A[i-1] == B[j-1])
dp[i][j] = dp[i-1][j-1]+1;
else //情况(2)
dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]);
}
}
public static void Buildsubs(char[] A) { //由dp构造subs
int k = dp[m][n]; //k为a和b的最长公共子序列的长度
int i=m;
int j=n;
while(k>0) { //在subs中放入最长公共子序列(反向)
if(dp[i][j] == dp[i-1][j]) //元素值等于上方相邻元素值
i--;
else if(dp[i][j] == dp[i][j-1]) //元素值等于左方相邻元素值
j--;
else { //与上方、左边元素的值均不相等
subs.add(String.valueOf(A[i-1]));//在subs中添加a[i-1]
i--;
j--;
k--;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
a = "ATCTGAT";
b = "TGCATA";
m = a.length();
n = b.length();
char[] A = a.toCharArray();
char[] B = b.toCharArray();
LCSlength(A, B); //求出dp
Buildsubs(A); //求出LCS
System.out.println("求解结果:");
System.out.println(" a:"+a);
System.out.println(" b:"+b);
System.out.print(" 最长公共子序列:");
for(int i=subs.size()-1; i>=0; i--) {
System.out.print(subs.get(i)+" ");//(new StringBuffer(subs.toString())).reverse() 也可以将字符串逆置
}
//System.out.print((new StringBuffer(subs.toString())).reverse()+"\n");
System.out.println("\n 长度:"+dp[m][n]);
}
}