最长公共子序列LCS 和 最长公众子串
51Nod-1006-最长公共子序列LCS
题目链接
http://www.51nod.com/Challenge/Problem.html#!#problemId=1006
题目
就是输入两个字符串str1
、str2
,输出任意一个最长公共子序列。
解析
dp[i][j]
代表的是 : 必须以str1[i]
、str2[j]
结尾的最长公共子序列,dp[i][j]
来源:
- 可能是
dp[i-1][j]
,代表str1[0~i-1]
与str2[0~j]
的最长公共子序列。 - 可能是
dp[i][j-1]
,代表str1[0~i]
与str2[0~j-1]
的最长公共子序列。 - 如果
str1[i] == str2[j]
,还可能是dp[i-1][j-1] + 1
。
这三种情况中取最大值。
构造结果的过程(利用dp
数组即可)
- 从矩阵的右下角开始,有三种移动方式: 向上、向左、向左上。
- 如果
dp[i][j] > dp[i-1][j] && dp[i][j] > dp[i][j-1]
,说明之前在计算dp[i][j]
的时候,一定是选择了dp[i-1][j-1]+1
,所以可以确定str1[i] = str2[j]
,并且这个字符一定输入最长公共子序列,把这个字符放进结果字符串,然后向左上方移动; - 如果
dp[i][j] == dp[i-1][j]
,说明之前计算dp[i][j]
的时候,dp[i-1][j-1]+1
不是必须的选择,向 上方移动即可; - 如果
dp[i][j] == dp[i][j-1]
,向 左方移动即可; - 如果
dp[i][j]
同时等于dp[i-1][j]
和dp[i][j-1]
,向上向左都可以,选择一个即可,不会错过必须选择的字符;
1 | import java.io.BufferedInputStream; |
最长公众子串
题目链接
https://www.nowcoder.com/questionTerminal/02e7cc263f8a49e8b1e1dc9c116f7602?toCommentId=1532408
解析
dp
矩阵第一列即dp[0~N-1][0]
,对某一个位置(i,0)
来说,如果str1[i] == str2[0]
,令dp[i][0] = 1
,否则令dp[i][0] = 0
;- 矩阵
dp
第一行,即dp[0][0~M-1]
,对某个位置(0,j)
来说,如果str1[0] == str2[j]
,令dp[0][j] = 1
,否则令dp[0][j] = 0
; - 一般的位置有两种情况,如果
str1[i] != str2[j]
,说明在必须把str1[i]
和str2[j]
当做公共子串最后一个字符是不可能的,所以dp[i][j] = 0
; 如果str1[i] = str2[j]
,说明可以将str1[i]
和str2[j]
作为公共子串的最后一个字符,其长度就是dp[i-1][j-1] + 1
;
1 | import java.util.*; |
由dp
表生成答案字符串也是不难的,找到最大值,然后往左边的res
个字符就是答案。
测试程序:
1 | public class LCSub { |
另外,还有一种可以优化空间的做法:
- 因为
dp[i][j]
只依赖于左上角位置的dp[i-1][j-1]
,所以用一个变量记录左上角的值即可。 - 遍历方向从右上角的斜线开始,一直遍历到左下角,中间记录最大值
max
和结束位置end
即可。
代码如下:
1 | public static String getLongestSubstring2(String sa,String sb){ |