1. 求两个数组的最长公共子序列
刻画最长公共子序列问题的最优子结构
设X=x1x2…xm和Y=y1y2…yn是两个序列,Z=z1z2…zk是这两个序列的一个最长公共子序列。
-
如果xm=yn,那么zk=xm=yn,且Zk-1是Xm-1,Yn-1的一个最长公共子序列;
-
如果xm≠yn,那么zk≠xm,意味着Z是Xm-1,Y的一个最长公共子序列;
-
如果xm≠yn,那么zk≠yn,意味着Z是X,Yn-1的一个最长公共子序列。
从上面三种情况可以看出,两个序列的LCS包含两个序列的前缀的LCS。因此,LCS问题具有最优子结构特征。
public static int process(int[] arr, int[] arr2, int i, int j){
if(i < 0 || j < 0){
return 0;
}
if(arr[i] == arr2[j]){
return 1 + process(arr, arr2, i - 1, j - 1);
}else{
return Math.max(process(arr, arr2, i - 1, j), process(arr, arr2, i, j - 1));
}
}
动态规划
dp[i][j]
的含义是:对于 s1[1..i]
和 s2[1..j]
,它们的 LCS 长度是 dp[i][j]
。
2. 求数组的最长上升子序列
https://leetcode-cn.com/problems/longest-harmonious-subsequence/
方法1
给定一个数组arr,数组是无序的,求该数组的最长上升子序列。
可以构造一个arr的副本arr2,对其进行排序,然后计算arr与arr2的最长公共子序列。
但是该方法不能用于数组中有重复元素的情况。比如arr= {2,2}。最终得到的结果为2。其实最终结果为1。
方法2 O(n^2)
设长度为N的数组为{a0,a1, a2, …an-1),则假定以aj结尾的数组序列的最长递增子序列长度为L(j),则L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是说,我们需要遍历在j之前的所有位置i(从0到j-1),找出满足条件a[i]<a[j]的L(i),求出max(L(i))+1即为L(j)的值。最后,我们遍历所有的L(j)(从0到N-1),找出最大值即为最大递增子序列。时间复杂度为O(N^2)。
例如给定的数组为{5,6,7,1,2,8},则L(0)=1, L(1)=2, L(2)=3, L(3)=1, L(4)=2, L(5)=4。所以该数组最长递增子序列长度为4,序列为{5,6,7,8}。
//代码是倒序遍历的。
public static int lengthOfLIS(int[] nums) {
if(null == nums || nums.length == 0){
return 0;
}
int len = nums.length;
int[] dp = new int[len];
int max = Integer.MIN_VALUE;
for(int i = len - 1; i >= 0; i--){
dp[i] = 1;
for(int j = i + 1; j < len; j++){
if(nums[j] > nums[i]){
if(dp[i] < dp[j] + 1)
dp[i] = dp[j] + 1;
}
}
max = Math.max(dp[i], max);
}
return max;
}
3. 无重复字符的最长子串
题目:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
本题可以使用窗口和HashMap<Character, Integer>
来解决,map中key为某个字符,value为该字符的下标。如果遍历到重复字符,用最新的下标覆盖旧的。
例如有字符串abcdbaefbc
,当窗口滑动到abcd
时,又要加入个b
字符,但是map中已经有b
字符了。那么此时窗口左边界就要跳到第一个b
字符的下一个字符的位置上。
滑动窗口在字符串上滑动的过程中,左边界是不回退的。例如有字符串abcdbaefbc
,当窗口滑动到cdb
时,下一个字符a
要加入窗口中,即要把<a,5>
加入到map中,但是map中已经有a
字符了,即<a, 0>
。此时窗口左边界为2,是大于第一个a
字符的索引的。此时直接将<a,5>
加入到map中,窗口继续向右扩展
public static int lengthOfLongestSubstring2(String s){
if(null == s) return 0;
HashMap<Character, Integer> map = new HashMap<>();
int left = 0, right = 0;
int max = 0;
while(left < s.length() && right < s.length()){
char temp = s.charAt(right);
if(map.containsKey(temp)){
max = Math.max(max, right - left);
left = Math.max(left, map.get(temp) + 1);
}
map.put(temp, right);
right++;
}
max = Math.max(max, right - left);
return max;
}
*4. 判断一个数是否为回文数
public static boolean isPalindrome(int x) {
if(x < 0 || (x % 10 == 0 && x != 0)){
return false;
}
//对数字x从各位逐位处理,得到后半部分翻转后的数字b
//当前半部分a不大于b时,说明b的位数已经占了数字x总位数的一半
//如1221,(a = 122, b = 1)-> (a = 12, b = 12)
//如12321,(a = 1232, b = 1) -> (a = 123, b = 12) -> (a = 12, b = 123)
int a = x, b = 0;
while(a > b){
b = b * 10 + a % 10;
a /= 10;
}
//当x位数为奇数时,b的位数比a的位数多一位
//当x位数为偶数时,b的位数与a的位数相等。
return a == b || a == b / 10;
}
5.1 买卖股票的最佳时机I
只能买卖一次。
使用单调栈有点大材小用
//遍历数组,将当前值减去当前已遍历元素中的最小值,得到最大的差值
class Solution {
public int maxProfit(int[] prices) {
int i = 0;
int valley = prices[0];
int peak = prices[0];
int maxprofit = 0;
while (i < prices.length - 1) {
while (i < prices.length - 1 && prices[i] >= prices[i + 1])
i++;
valley = prices[i];
while (i < prices.length - 1 && prices[i] <= prices[i + 1])
i++;
peak = prices[i];
maxprofit += peak - valley;
}
return maxprofit;
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-ii-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5.2 买卖股票的最佳时机II
可以买多次
使用单调栈依然有点多余,还是第一题的思路。只不过每遇到一个元素小于它的前一个元素,就会完成一次交易…
public static int maxProfit2(int[] prices) {
if(prices == null || prices.length == 0) return 0;
int profit = 0;
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
if(prices[i] < prices[i - 1]){
profit += prices[i - 1] - min;
min = prices[i];
}
}
return profit + prices[prices.length - 1] - min;
}
*5.3 买卖股票的最佳时机III
6 最长回文子串
自己写一遍
动态规划 和 中心扩散法
public class Solution_5 {
/**
* 中心扩散法
* @param s
* @return
*/
public static String longestPalindrome(String s) {
if(s == null || s.length() == 0){
return "";
}
int len = s.length();
String result = s.charAt(0) + "";
int max = 1;
for(int i = 0; i < len; i++){
//aba
int len1 = aba(s, i - 1, i + 1);;
//aa
int len2 = aba(s, i, i+ 1);
if(Math.max(len1, len2) > max){
max = Math.max(len1, len2);
if(len1 > len2){
int radius = len1 >> 1;
result = s.substring(i - radius, i + radius + 1);
}else{
int radius = len2 >> 1;
result = s.substring(i - radius + 1, i + radius + 1);
}
}
}
return result;
}
//返回回文字符串的长度
public static int aba(String s, int left, int right){
int len = s.length();
while(left >= 0 && right < len){
if(s.charAt(left) == s.charAt(right)){
left--;
right++;
}else{
break;
}
}
return right - left - 1;
}
public static void main(String[] args) {
String s = "ababad";
System.out.println(longestPalindrome(s));
}
}