本篇为使用动态规划解决的的求字符串中的一些子串或子序列题的集合。
Longest Increasing Subsequence
题目为求一个数组最长的上升子序列。我们使用动规来解决该问题,使用一个f[i]存放前i个数的最长上升子序列,如此可将原问题划分为若干个小问题。对于每个位置的i,我们检测它之前的位置如果有比他小的数,我们认为之前的f是可以用上的,这时我们取最大的就好了。代码:
public int longestIncreasingSubsequence(int[] nums) {
int []f = new int[nums.length];
int max = 0;
for (int i = 0; i < nums.length; i++) {
f[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[j] <= nums[i]) {
f[i] = f[i] > f[j] + 1 ? f[i] : f[j] + 1;
}
}
if (f[i] > max) {
max = f[i];
}
}
return max;
}
Longest Consecutive Sequence(不使用动规)
这道题为求数组中最长的连续序列。这道题不使用动规,因为我们不知道连续的序列数出现的位置,同时还可能存在多个序列,不知道要选择哪个一个所以没有办法分解成小的子问题。不过我们可以使用一个HashSet来优化一下,这样确保每个数只搜索一次即可,时间复杂度为O(n)。
public int longestConsecutive(int[] nums) {
public int longestConsecutive(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
set.add(nums[i]);
}
int longest = 0;
for (int i = 0; i < nums.length; i++) {
int down = nums[i] - 1;
while (set.contains(down)) {
set.remove(down);
down--;
}
int up = nums[i] + 1;
while (set.contains(up)) {
set.remove(up);
up++;
}
longest = Math.max(longest, up - down - 1);
}
return longest;
}<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
set.add(nums[i]);
}
int longest = 0;
for (int i = 0; i < nums.length; i++) {
int down = nums[i] - 1;
while (set.contains(down)) {
set.remove(down);
down--;
}
int up = nums[i] + 1;
while (set.contains(up)) {
set.remove(up);
up++;
}
longest = Math.max(longest, up - down - 1);
}
return longest;
}
Longest Common Substring
题目为求两个字符串的最长公共子串。这道题可以使用动规也可以不使用,由于公共子串是连续,使用递归可能也不起到优化的效果,因此,我们使用最简单的方法,对于第一个字符串的每个位置,我们都使用一个循环来检测子串:
while (i + len < xlen && j + len < ylen &&
A.charAt(i + len) == B.charAt(j + len))
len ++;
if(len > maxlen)
maxlen = len;
}
Longest Common Subsequence
题目为求两个字符串的公共子序列。子序列与子串的区别其实在于子序列不是连续的,而子串必须是连续的。这道题就可以使用动规方法了,因为对比两个字符串中的目前存在的子序列就可以将问题分成小块的问题了。使用一个f[i][j]用来保存第一个字符串的前i个字符与第二个字符串的前j个字符的公共子序列长度,然后进行递归:
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
if(A.charAt(i - 1) == B.charAt(j - 1))
f[i][j] = f[i - 1][j - 1] + 1;
}
}
Longest Common Prefix
题目为若干个个字符串的最长公共子前缀。我们先保存第一个字符串作为子前缀的初始值,然后逐一比较,有可能是逐步缩小子前缀,那么最后留下来的子前缀即为全部字符串的公共子前缀了:
prefix = strs[0];
for(int i = 1; i < strs.length; i++) {
int j = 0;
while( j < strs[i].length() && j < prefix.length() && strs[i].charAt(j) == prefix.charAt(j)) {
j++;
}
if( j == 0) {
return "";
}
prefix = prefix.substring(0, j);
}