字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。
令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列
,使得对所有的j=0,1,…,k-1,有xij=yj。
例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
依旧按照动态规划的五个步骤来求解此题。
2、确定动态规划方程。设两个字符串为text1和text2。
用dp[i][j]来表示text1中0~i-1区间的字符串和text2中0~j-1区间的字符串的最长公共子序列,区间为闭区间,也就是索引为0的字符和索引为i-1的字符可以取到。
另外还要注意的是i-1和j-1。
3、正确地初始化dp数组。情况一:当text[i-1]=text[j-1]时,最长公共子序列的长度应该增加一,即dp[i][j]=dp[i-1][j-1]+1;
情况二:当text[i-1]!=text[j-1]时,最长公共子序列的长度应该为dp[i-1][j]或者dp[i][j-1]。
比如说有两个字符串bdc和bd,它们的最长公共子序列的长度应该是①bdc和b最长公共子序列长度1,②bd和bd的最长公共子序列的长度2。
两者之间取一个最大值即为dp[i][j]的值。
综上所述,动态规划方程为:
当text[i-1]=text[j-1]时,dp[i][j]=dp[i-1][j-1]+1;
当text[i-1]!=text[j-1]时,dp[i][j]=max(dp[i-1][j],dp[i][j-1])。
4、确定遍历顺序。m为字符串1的长度,n为字符串2的长度,因此为dp数组开辟空间的时候一定要注意。
dp数组肯定设为二维数组。
因为dp[i][j]表示的是text1中0~i-1区间的字符串和text2中0~j-1区间的字符串的最长公共子序列,所以最后返回的是dp[m][n]。
当其中一个字符串取空串时,它和另外一个字符串的最长公共子序列长度一定为0。
也就是dp[0][j]=0,dp[i][0]=0。
因为初始化的时候数组默认为0,也就不用再另外赋值了。
dp[0][0]自然也为0,讨论下去也没什么意义。
5、举例推导dp数组,验证动态方程。因为dp[i][j]依赖于dp[i-1][j-1],dp[i-1][j],dp[i][j-1]。
遍历顺序也就相应的确定为从前往后遍历,并且从i=1和j=1的时候开始遍历。
下面为代码段,Java语言实现。
public int longestCommonSubsequence(String text1, String text2) { int m = text1.length(); int n = text2.length(); int [][]dp = new int[m+1][n+1]; for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(text1.charAt(i-1)==text2.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]+1; } else { dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]); } } } return dp[m][n]; }
如果有错误还请大家指出,我会虚心改正。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)