问题
- 给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度。
- 一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
- 若这两个字符串没有公共子序列,则返回 0。
问题分析
本题是求两个字符串的最长公共子序列,是求存在的最优值,可判断为动态规划问题,解决问题的基本步骤为:
- 确定状态(开一个数组)
db[i][j]:长度为[0…i]的字符串text1和长度为[0…j]的字符串text2的公共子序列的长度为db[i][j]
- 确定状态转移方程
db数组大小为int[n][m],两字符串长度为n-1,m-1
这里我们把两个字符串的最后一个字符去掉,前面剩下的构成两个子字符串,如果两个子字符串的最长公共子序列已经找到,那么它们的最长公共子序列长度存储在db[n-1][m-1]中,那么加上最后一个字符后原字符串的最长公共子序列为:
(1)最后两元素相等 text1.charAt(n-1)==text2.charAt(m-1) 所以db[n][m]=db[n-1][m-1]+1
(2)最后两元素不等 db[n][m]=max{db[n][m-1],db[]n-1[m]}
综上得到状态转移方程为:
{ d b [ i ] [ j ] = d b [ i − 1 ] [ j − 1 ] + 1 t e x t 1. c h a r A t ( i − 1 ) = t e x t 2. c h a r A t ( j − 1 ) d b [ i ] [ j ] = M a t h . m a x ( d b [ i ] [ j − 1 ] , d b [ i − 1 ] [ j ] ) t e x t 1. c h a r A t ( i − 1 ) ≠ t e x t 2. c h a r A t ( j − 1 ) \left\{ \begin{aligned} db[i][j]&=db[i-1][j-1]+1&text1.charAt(i-1)=text2.charAt(j-1) \\ db[i][j]&=Math.max(db[i][j-1],db[i-1][j]) &text1.charAt(i-1)\not=text2.charAt(j-1) \end{aligned} \right. {db[i][j]db[i][j]=db[i−1][j−1]+1=Math.max(db[i][j−1],db[i−1][j])text1.charAt(i−1)=text2.charAt(j−1)text1.charAt(i−1)=text2.charAt(j−1)
- 初始化和边界
(1)当任意一个字符串为空时,最长公共子序列长度都为零,所以db[0][0…m]和db[0…n][0]都应该初始化为零
(2)注意db数组和字符串数组的大小,避免数组越界
- 确定计算顺序
从状态转移方程可以看出,获取db[i][j]的值有三个方向
所以我们运算的顺序为从左到右,从上到下
代码
public static int LongestCommonSubsequence(String text1,String text2){
int len1=text1.length(),len2=text2.length();
//开一个数组
int [][]db=new int [len1+1][len2+1];
//初始化
for(int i=0;i<=len1;i++){
db[i][0]=0;
}
for(int j=0;j<=len2;j++){
db[0][j]=0;
}
for(int i=1;i<=len1;i++){
for(int
j=1;j<=len2;j++){
if(text1.charAt(i-1)==text2.charAt(j-1)){
db[i][j]=db[i-1][j-1]+1;
}
else{
db[i][j]=Math.max(db[i][j-1],db[i-1][j]);
}
}
}
return db[len1][len2];
}