leetcode-子序列 子串

1. 求两个数组的最长公共子序列

刻画最长公共子序列问题的最优子结构
设X=x1x2…xm和Y=y1y2…yn是两个序列,Z=z1z2…zk是这两个序列的一个最长公共子序列。

  1.  如果xm=yn,那么zk=xm=yn,且Zk-1是Xm-1,Yn-1的一个最长公共子序列;
    
  2.  如果xm≠yn,那么zk≠xm,意味着Z是Xm-1,Y的一个最长公共子序列;
    
  3.  如果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

买卖股票的最佳时机 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));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值