0
点赞
收藏
分享

微信扫一扫

NC刷题笔记10-动态规划

夏木之下 2022-04-02 阅读 62

NC刷题笔记10-动态规划

BM62 斐波那契数列

思路:
	递归
	非递归
public class Solution {
    public int Fibonacci(int n) {
        if(n<=2) return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}
public class Solution {
    public int Fibonacci(int n) {
        if(n<=2) return 1;
        int a=1,b=1,sum=a+b;
        for(int i=3;i<=n;i++){
            sum=a+b;
            a=b;
            b=sum;
        }
        return sum;
    }
}

BM63 跳台阶

描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:1≤n≤40
要求:时间复杂度:O(n) ,空间复杂度: O(1)

示例1
输入:2
返回值:2
说明:青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2       

示例2
输入:7
返回值:21
public class Solution {
    public int jumpFloor(int target) {
        if(target<=1) return 1;
        int a=1,b=1,sum=a+b;
        for(int i=2;i<=target;i++){
            sum=a+b;
            a=b;
            b=sum;
        }
        return sum;
    }
}

BM64 最小花费爬楼梯

描述
给定一个整数数组 cost   ,其中 cost[i]是从楼梯第i个台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
数据范围:数组长度满足 1≤n≤10^5,数组中的值满足 1≤cost≤10^4
  
示例1
输入:[2,5,20]
返回值:5
说明:你将从下标为1的台阶开始,支付5 ,向上爬两个台阶,到达楼梯顶部。总花费为5   

示例2
输入:[1,100,1,1,1,90,1,1,80,1]
返回值:6

说明:
你将从下标为 0 的台阶开始。
1.支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
2.支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
3.支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
4.支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
5.支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
6.支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6
import java.util. ;
public class Solution {
    public int minCostClimbingStairs (int[] cost) {
        int n=cost.length;
        if(n==1||n==2) return 0;
        int[] dp=new int[n+1];
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<=n;i++){
            dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[n];
    }
}

BM65 最长公共子序列(二)

描述
给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列
数据范围:0≤∣str1∣,∣str2∣≤2000
要求:空间复杂度 O(n^2),时间复杂度 O(n^2)

示例1
输入:"1A2C3D4B56","B1D23A456A"
返回值:"123456"

示例2
输入:"abc","def"
返回值:"-1"

示例3
输入:"abc","abc"
返回值:"abc"

示例4
输入:"ab",""
返回值:"-1"
import java.util. ;

public class Solution {
    public String LCS (String s1, String s2) {
        int n=s1.length();
        int m=s2.length();
        String[][] dp=new String[n+1][m+1];
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                if(i==0||j==0){
                    dp[i][j]="";
                }else if(s1.charAt(i-1)==s2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1]+s1.charAt(i-1);
                }else{
                    dp[i][j]=dp[i-1][j].length()>dp[i][j-1].length()?dp[i-1][j]:dp[i][j-1];
                }
            }
        }
        return dp[n][m]==""?"-1":dp[n][m];
    }
}

BM66 最长公共子串

描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串题目保证str1和str2的最长公共子串存在且唯一。 
数据范围:1≤∣str1∣,∣str2∣≤5000
要求: 空间复杂度 O(n^2),时间复杂度 O(n^2)

示例1
输入:"1AB2345CD","12345EF"
返回值:"2345"

备注:1≤∣str1∣,∣str2∣≤5000
思路:
	动态规划
	某位置上相等: dp[i + 1][j + 1] = dp[i][j] + 1;
	某位置上不等: dp[i + 1][j+1] = 0;
public String LCS(String str1, String str2) {
    int maxLenth = 0;//记录最长公共子串的长度
    //记录最长公共子串最后一个元素在字符串str1中的位置
    int maxLastIndex = 0;
    int[][] dp = new int[str1.length() + 1][str2.length() + 1];
    for (int i = 0; i < str1.length(); i++) {
        for (int j = 0; j < str2.length(); j++) {
            //递推公式,两个字符相等的情况
            if (str1.charAt(i) == str2.charAt(j)) {
                dp[i + 1][j + 1] = dp[i][j] + 1;
                //如果遇到了更长的子串,要更新,记录最长子串的长度,
                //以及最长子串最后一个元素的位置
                if (dp[i + 1][j + 1] > maxLenth) {
                    maxLenth = dp[i + 1][j+1];
                    maxLastIndex = i;
                }
            } else {
                //递推公式,两个字符不相等的情况
                dp[i + 1][j+1] = 0;
            }
        }
    }
    //最字符串进行截取,substring(a,b)中a和b分别表示截取的开始和结束位置
    return str1.substring(maxLastIndex - maxLenth + 1, maxLastIndex + 1);
}

BM67 不同路径的数目(一)

描述
一个机器人在m×n大小的地图的左上角(起点)。机器人每次可以向下或向右移动。机器人要到达地图的右下角(终点)。可以有多少种不同的路径从起点走到终点?
备注:m和n小于等于100,并保证计算结果在int范围内   数据范围:0<n,m≤100,保证计算结果在32位整型范围内

要求:空间复杂度O(nm),时间复杂度O(nm)
进阶:空间复杂度O(1),时间复杂度O(min(n,m))

示例1
输入:2,1
返回值:1

示例2
输入:2,2
返回值:2
思路:
	动态规划:dp[i][j]=dp[i-1][j]+dp[i][j-1];
	每个位置只会从左边或者上边过来
import java.util. ;

public class Solution {
    public int uniquePaths (int m, int n) {
        int[][] dp=new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(i==1||j==1){
                    dp[i][j]=1;
                }else{
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        return dp[m][n];
    }
}

BM68 矩阵的最小路径和

描述
给定一个 n   m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。
数据范围: 1≤n,m≤500,矩阵中任意值都满足0≤ai,j≤100

要求:时间复杂度 O(nm)

例如:当输入[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]时,对应的返回值为12,
所选择的最小累加和路径如下图所示:

在这里插入图片描述

示例1
输入:[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]
返回值:12

示例2
输入:[[1,2,3],[1,2,3]]
返回值:7
思路:
	动态规划
	1 上边只能从左边过来
	2 左边只能从上边过来
	3 其他的可以从上左两个方向过来
import java.util. ;
public class Solution {
    public int minPathSum (int[][] matrix) {
        if(matrix==null||matrix.length==0) return 0;
        int n=matrix.length;
        int m=matrix[0].length;
        int[][] dp=new int[n+1][m+1];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(i==1){
                    dp[i][j]=dp[i][j-1]+matrix[i-1][j-1];
                }else if(j==1){
                    dp[i][j]=dp[i-1][j]+matrix[i-1][j-1];
                }else{
                    dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+matrix[i-1][j-1];
                }
            }
        }
        return dp[n][m];
    }
}

BM69 把数字翻译成字符串

描述
有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。
现在给一串数字,返回有多少种可能的译码结果
数据范围:字符串长度满足0<n≤90
进阶:空间复杂度O(n),时间复杂度O(n)

示例1
输入:"12"
返回值:2
说明:2种可能的译码结果(”ab” 或”l”)  

示例2
输入:"31717126241541717"
返回值:192

说明:192种可能的译码结果
动态规划:
四种情况
1 无法翻译 当前为0 前面的>2或前面也为0
2 只能组合翻译 当前为0 前面为1或2
3 只能单独翻译 当前>6 前面为0或者前面>2
4 可以组合翻译 dp[i]=dp[i-1]+dp[i-2]
import java.util. ;
public class Solution {
    public int solve (String nums) {
        if(nums==null ||nums.length()==0) return 0;
        int[] dp = new int[nums.length()+1];
        dp[0]=1;
        dp[1]=nums.charAt(0)=='0'?0:1;
        for(int i=2;i<dp.length;i++){
            //无法独立编码也无法组合编码
            if(nums.charAt(i-1)=='0' && (nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2')){
                return 0;
            //只能组合编码
            }else if(nums.charAt(i-1)=='0'){
                dp[i] = dp[i-2];
            //只能独立编码
            }else if(nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2' || nums.charAt(i-2)=='2'&& nums.charAt(i-1)>'6' ){
                dp[i] = dp[i-1];
            //两种编码方式都可以
            }else{
                dp[i] = dp[i-1]+dp[i-2];
            }
        }
        return dp[nums.length()];
    }
}

BM70 兑换零钱(一)

描述
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。 如果无解,请返回-1.

数据范围:数组大小满足0≤n≤10000 , 数组中每个数字都满足0<val≤10000,0≤aim≤5000
要求:时间复杂度O(n×aim) ,空间复杂度O(aim)

示例1
输入:[5,2,3],20
返回值:4

示例2
输入:[5,2,3],0
返回值:0

示例3
输入:[3,5],2
返回值:-1
思路:
	动态规划
	dp[i] = Math.min(dp[i],dp[i-arr[j]] + 1);
import java.util. ;
public class Solution {
    public int minMoney (int[] arr, int aim) {
        int Max = aim + 1;//定一个全局最大值
        int []dp = new int[aim + 1];//dp[i]的含义是目标值为i的时候最少钱币数是多少。
        Arrays.fill(dp,Max);//把dp数组全部定为最大值
        dp[0] = 0;//总金额为0的时候所需钱币数一定是0
        for(int i = 1;i <= aim;i ++){// 遍历目标值
            for(int j = 0;j < arr.length;j ++){// 遍历钱币
                if(arr[j] <= i){//如果当前的钱币比目标值小就可以兑换
                    dp[i] = Math.min(dp[i],dp[i-arr[j]] + 1);
                }
            }
        }
        return dp[aim] > aim ? -1 : dp[aim];
    }
}

BM71 最长上升子序列(一)

描述
给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。
我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 i 和 j 满足 i<j 且 arri≥arrj。
数据范围: 0≤n≤1000
要求:时间复杂度 O(n^2), 空间复杂度 O(n)

示例1
输入:[6,3,1,5,2,3,7]
返回值:4
说明:该数组最长上升子序列为 [1,2,3,7] ,长度为4
思路:
动态规划
	遍历0到i-1位置的元素,如果有小于i位置的元素 
	则dp[i]=Math.max(dp[i],dp[j]+1);
	返回最大值即可
import java.util. ;

public class Solution {
    public int LIS (int[] arr) {
        if(arr==null||arr.length==0) return 0;
        int maxLen=1;
        int[] dp=new int[arr.length];
        Arrays.fill(dp,1);
        for(int i=1;i<dp.length;i++){
            for(int j=i-1;j>=0;j--){
                if(arr[i]>arr[j]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            maxLen=Math.max(maxLen,dp[i]);
        }
        return maxLen;
    }
}

BM72 连续子数组的最大和

描述
输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。
数据范围:1<=n<=2 10^5 , −100<=a[i]<=100
要求:时间复杂度为 O(n),空间复杂度为 O(n)
进阶:时间复杂度为 O(n),空间复杂度为 O(1)

示例1
输入:[1,-2,3,10,-4,7,2,-5]
返回值:18
说明:经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18        

示例2
输入:[2]
返回值:2

示例3
输入:[-10]
返回值:-10
思路1:
	动态规划 dp[i]=array[i-1]+((dp[i-1]>0)?dp[i-1]:0);
	时间复杂度O(n) 空间复杂度 O(n)

思路2:
	思路1的改进,dp状态只需要1个,前一个元素的dp值
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null || array.length==0) return 0;
        int[] dp=new int[array.length+1];
        int maxres=Integer.MIN_VALUE;
        for(int i=1;i<=array.length;i++){
            dp[i]=array[i-1]+((dp[i-1]>0)?dp[i-1]:0);
            maxres=Math.max(maxres,dp[i]);
        }
        return maxres;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null || array.length==0) return 0;
        int b=0;
        int maxSum=Integer.MIN_VALUE;
        for(int i=0;i<array.length;i++){
            b=array[i]+(b>0?b:0);
            maxSum=Math.max(b,maxSum);
        }
        return maxSum;
    }
}

BM73 最长回文子串

描述
对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度。数据范围:1≤n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n^2)
进阶:  空间复杂度 O(n),时间复杂度 O(n)

示例1
输入:"ababc"
返回值:3
说明:最长的回文子串为"aba"与"bab",长度都为3

示例2
输入:"abbba"
返回值:5

示例3
输入:"b"
返回值:1
思路1:
	中心扩散法,需要判断 双数和单数两种情况
	时间复杂度:O(N^2),空间复杂度:O(1)
思路2:
	双层for循环+双指针
	时间复杂度:O(N^2),空间复杂度:O(1)
思路3:
	动态规划
	两层for循环
	dp[left][right] = dp[left + 1][right - 1];
	时间复杂度:O(N^2),空间复杂度:O(N^2)
import java.util. ;
public class Solution {
    public int getLongestPalindrome(String A) {
        char[] chars=A.toCharArray();
        int maxLength=0;
        for(int i=0;i<chars.length;i++){
            maxLength=Math.max(maxLength,getLength(i,chars));
        }
        return maxLength;
    }
    
    public int getLength(int i,char[] chars){
        //单数
        int res=1,left=i-1,right=i+1;
        while(left>=0 && right<chars.length){
            if(chars[left]==chars[right]){
                res+=2;
                left--;
                right++;
            }else{
                break;
            }
        }
        //双数
        int res0=0;
        if((i+1)<chars.length && chars[i]==chars[i+1]){
            left=i;
            right=i+1;
            while(left>=0 && right<chars.length){
                if(chars[left]==chars[right]){
                    res0+=2;
                    left--;
                    right++;
                }else{
                    break;
                }
                
            }
        }
        return Math.max(res,res0);
    }
}
import java.util. ;
public class Solution {
    public int getLongestPalindrome(String A) {
        char[] chars=A.toCharArray();
        int maxlength=0;
        for(int i=0;i<chars.length;i++){
            for(int j=i;j<chars.length;j++){
                if(judge(i,j,chars)) maxlength=Math.max(maxlength,j-i+1);
            }
        }
        return maxlength;
    }
    
    public boolean judge(int i,int j,char[] chars){
        if(i==j) return true;
        while(i<j){
            if(chars[i]!=chars[j]) return false;
            i++;
            j--;
        }
        return true;
    }
}
public int getLongestPalindrome(String A, int n) {
    //边界条件判断
    if (n < 2) return A.length();
    //start表示最长回文串开始的位置,
    //maxLen表示最长回文串的长度
    int maxLen = 1;
    boolean[][] dp = new boolean[n][n];
    for (int right = 1; right < n; right++) {
        for (int left = 0; left <= right; left++) {
            //如果两种字符不相同,肯定不能构成回文子串
            if (A.charAt(left) != A.charAt(right)) continue;
            //下面是s.charAt(left)和s.charAt(right)两个
            //字符相同情况下的判断
            //如果只有一个字符,肯定是回文子串
            if (right == left) {
                dp[left][right] = true;
            } else if (right - left <= 2) {
                //类似于"aa"和"aba",也是回文子串
                dp[left][right] = true;
            } else {
                //类似于"a      a",要判断他是否是回文子串,只需要
                //判断"      "是否是回文子串即可
                dp[left][right] = dp[left + 1][right - 1];
            }
            //如果字符串从left到right是回文子串,只需要保存最长的即可
            if (dp[left][right] && right - left + 1 > maxLen) {
                maxLen = right - left + 1;
            }
        }
    }
    //最长的回文子串
    return maxLen;
}

BM74 数字字符串转化成IP地址

描述
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。

例如:
给出的字符串为"25525522135",
返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)

数据范围:字符串长度 0≤n≤12  要求:空间复杂度O(n!),时间复杂度 O(n!)

注意:ip地址是由四段数字组成的数字序列,格式如 "x.x.x.x",其中 x 的范围应当是 [0,255]。

示例1
输入:"25525522135"
返回值:["255.255.22.135","255.255.221.35"]

示例2
输入:"1111"
返回值:["1.1.1.1"]

示例3
输入:"000256"
返回值:"[]"
思路1:
	暴力枚举,每一个位包含一到三位字符
	如果四个位置相加为s.length
思路2:
	递归回溯
思路1import java.util. ;
public class Solution {
    public ArrayList<String> restoreIpAddresses (String s) {
        ArrayList<String> list = new ArrayList();
        for(int a=1; a<4; a++){
            for(int b=1; b<4; b++){
                for(int c=1; c<4; c++){
                    for(int d=1; d<4; d++){
                        if(a+b+c+d==s.length()){
                            String s1 = s.substring(0, a);
                            String s2 = s.substring(a, a+b);
                            String s3 = s.substring(a+b, a+b+c);
                            String s4 = s.substring(a+b+c, a+b+c+d);
                            if(check(s1)&&check(s2)&&check(s3)&&check(s4)){
                                String ip = s1+"."+s2+"."+s3+"."+s4;
                                list.add(ip);
                            }
                        }
                    }
                }
            }
        }
        return list;
    }

    public boolean check(String s){
        if(Integer.valueOf(s)<=255){
            if(s.charAt(0)!='0' || s.charAt(0)=='0'&&s.length()==1) 
                return true;
        }
        return false;
    }
}
思路2import java.util. ;
public class Solution {
    public ArrayList<String> restoreIpAddresses (String s) {
        if (s.length() > 12) {
            return null;
        }
        StringBuilder sb = new StringBuilder(s);
        doRestoreIpAddresses(sb, 1);
        return ans;
    }
    ArrayList<String> ans = new ArrayList<>();
    public void doRestoreIpAddresses(StringBuilder s, int m) { // 回溯暴力算法,还要很多地方没有剪枝
        String[] nums = s.toString().split("\\.");
        if (nums.length > 4) { // 剪枝
            return;
        }
        if (validateIPv4(s.toString())) { // 结束条件
            ans.add(String.copyValueOf(s.toString().toCharArray()));
            return;
        }

        for (int i = m; i < s.length(); i++) { // 回溯
            s.insert(i, "."); 
            doRestoreIpAddresses(s, i + 2);
            s.replace(i, i + 1, "");

        }
    }
    
     public boolean validateIPv4(String IP) {
        String[] nums = IP.split("\\.");
        if (nums.length !=  4) {
            return false;
        }
        for (String x : nums) {
            // 0-255:
            if (x.length() == 0 || x.length() > 3) return false;
            // 0的情况
            if (x.charAt(0) == '0' && x.length() != 1) return false;
            // 大于255
            if (Integer.parseInt(x) > 255) return false;
        }
        return true;
    }
}

BM75 编辑距离(一)

描述
给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数。
你可以对字符串进行3种操作:
1.插入一个字符
2.删除一个字符
3.修改一个字符。

字符串长度满足1≤n≤1000,保证字符串中只出现小写英文字母。

示例1
输入:"nowcoder","new"
返回值:6
说明:"nowcoder"=>"newcoder"(将'o'替换为'e'),修改操作1次     "nowcoder"=>"new"(删除"coder"),删除操作5次      

示例2
输入:"intention","execution"
返回值:5
说明:一种方案为:因为2个长度都是9,后面的4个后缀的长度都为"tion",于是从"inten"到"execu"逐个修改即可  

示例3
输入:"now","nowcoder"
返回值:5
思路:
	动态规划
	  int[][] dp=new int[str1.length()+1][str2.length()+1];
	  
	  当前位置字符相等 
		dp[i+1][j+1]=dp[i][j];
	  当前位置字符不相等 
		dp[i+1][j+1]=Math.min(insert,delete,replace)
		int delete=dp[i+1][j]+1;
         int insert=dp[i][j+1]+1;
         int change=dp[i][j]+1;
import java.util. ;

public class Solution {
    public int editDistance (String str1, String str2) {
        if(str1.length()==0||str2.length()==0) return str1.length()==0?str2.length():str1.length();
        int[][] dp=new int[str1.length()+1][str2.length()+1];
        //特殊解
        for(int i=0;i<=str1.length();i++){
            dp[i][0]=i;
        }
        for(int i=0;i<=str2.length();i++){
            dp[0][i]=i;
        }
        
        for(int i=0;i<str1.length();i++){
            for(int j=0;j<str2.length();j++){
                if(str1.charAt(i)==str2.charAt(j)){
                    dp[i+1][j+1]=dp[i][j];
                }else{
                    int delete=dp[i+1][j]+1;
                    int insert=dp[i][j+1]+1;
                    int change=dp[i][j]+1;
                    int min = change > insert ? insert : change;
                    min = delete > min ? min : delete;
                    dp[i + 1][j + 1] = min;
                }
            }
        }
        return dp[str1.length()][str2.length()];
    }
}

BM76 正则表达式匹配

描述
请实现一个函数用来匹配包括'.'和' '的正则表达式。
1.模式中的字符'.'表示任意一个字符
2.模式中的字符' '表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab ac a"匹配,但是与"aa.a"和"ab a"均不匹配

数据范围:
1.str 只包含从 a-z 的小写字母。
2.pattern 只包含从 a-z 的小写字母以及字符 . 和  ,无连续的 ' '。
3. 0≤str.length≤26 
4. 0≤pattern.length≤26 


示例1
输入:"aaa","a a"
返回值:true
说明:中间的 可以出现任意次的a,所以可以出现1次a,能匹配上              

示例2
输入:"aad","c a d"
返回值:true
说明:因为这里 c 为 0 个,a被重复一次,   表示零个或多个a。因此可以匹配字符串 "aad"。              

示例3
输入:"a",". "
返回值:true
说明:". " 表示可匹配零个或多个(' ')任意字符('.')              

示例4
输入:"aaab","a a a c"
返回值:false
思路:
	动态规划
	1、 i和j处的字符相等 dp[i][j]=d[i-1][j-1]
	2、 j处为'.' dp[i][j]=d[i-1][j-1]
	3、 j处为' ' j-1处不为'.'
	  匹配成功
		3.1 匹配0次 dp[i][j]=dp[i][j-2]
 		3.2 匹配1次 dp[i][j]=d[i][j-1]
 		3.3 匹配n次 d[i][j]=d[i-1][j]
 	  匹配不成功
 	  	3.4 匹配0次 dp[i][j]=dp[i][j-2]
 	4、j-1处为'.' j处为' '
		4.1 匹配0次 dp[i][j]=dp[i][j-2]
 		4.2 匹配1次 dp[i][j]=d[i][j-1]
 		4.3 匹配n次 d[i][j]=d[i-1][j]
import java.util. ;


public class Solution {
    public boolean match (String str, String pattern) {
        boolean[][] dp=new boolean[str.length()+1][pattern.length()+1];
        dp[0][0]=true;
        for(int i=1;i<=pattern.length();i++){
            //匹配0次
            if (i >= 2 && pattern.charAt(i - 1) == ' ') dp[0][i] = dp[0][i - 2];
        }
        
        for(int i=1;i<=str.length();i++){
            for(int j=1;j<=pattern.length();j++){
                //1 字符相等
                if(str.charAt(i-1)==pattern.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1];
                //2 j为'.'
                }else if(pattern.charAt(j-1)=='.'){
                    dp[i][j]=dp[i-1][j-1];
                //3 j为' '
                }else if(pattern.charAt(j-1)==' '){
                    //3.1 j为' ' j-1为'.'
                    if(j>=2 && pattern.charAt(j-2)=='.'){
                        dp[i][j]=dp[i][j-2]||dp[i][j-1]||dp[i-1][j];
                    //3.2 j为' ' j-1不为'.'
                    }else{
                        //1 匹配成功
                        if(j>=2 && str.charAt(i-1)==pattern.charAt(j-2)){
                            dp[i][j]=dp[i][j-2]||dp[i][j-1]||dp[i-1][j];
                        //2 匹配不成功
                        }else{
                            dp[i][j]=dp[i][j-2];
                        }
                    }
                }
            }
        }
        return dp[str.length()][pattern.length()];
    }
}

BM77 最长的括号子串

描述
给出一个长度为 n 的,仅包含字符 '(' 和 ')' 的字符串,计算最长的格式正确的括号子串的长度。
例1: 对于字符串 "(()" 来说,最长的格式正确的子串是 "()" ,长度为 2 .
例2:对于字符串 ")()())" , 来说, 最长的格式正确的子串是 "()()" ,长度为 4 .
字符串长度:0≤n≤5∗10 5

要求时间复杂度 O(n) ,空间复杂度 O(n).
示例1
输入:"(()"
返回值:2

示例2
输入:"(())"
返回值:4
思路:
	动态规划
	当前位置i为( dp为0
	当前位置i为) 需要判断
		1、i-1为( ,dp[i]=dp[i-2]+2
		2、i-1不为(,i-dp[i-1]-1=='(' dp[i]=dp[i-dp[i-1]-2]+dp[i-1]+2
import java.util. ;

public class Solution {
    public int longestValidParentheses (String s) {
        int maxans = 0;
        int[] dp = new int[s.length()];
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                maxans = Math.max(maxans, dp[i]);
            }
        }
        return maxans;
    }
}

BM78 打家劫舍(一)

描述
你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。
给定一个整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。
数据范围:数组长度满足1≤n≤2×10^5,数组中每个值满足1≤num[i]5000 
示例1
输入:[1,2,3,4]
返回值:6
说明:最优方案是偷第 24 个房间   

示例2
输入:[1,3,6]
返回值:7
说明:最优方案是偷第 13个房间   

示例3
输入:[2,10,5]
返回值:10
说明:最优方案是偷第 2 个房间
思路:
	动态规划
	每天有两种状态 偷和不偷
		偷 dp[i-1][1]+nums[i-1];  左边不偷+今天
		不偷 dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]);  昨天投和不偷的最大值
import java.util. ;

public class Solution {
    public int rob (int[] nums) {
        int[][] dp=new int[nums.length+1][2];
        dp[0][0]=0;
        dp[0][1]=0;
        int max=0;
        for(int i=1;i<=nums.length;i++){
            dp[i][0]=dp[i-1][1]+nums[i-1];
            dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]);
            max=Math.max(dp[i][0],dp[i][1]);
        }
        return max;
    }
}

BM79 打家劫舍(二)

描述
你是一个经验丰富的小偷,准备偷沿湖的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。
给定一个长度为n的整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。
数据范围:数组长度满足1≤n≤2×10^5,数组中每个值满足1≤nums[i]≤5000 

示例1
输入:[1,2,3,4]
返回值:6
说明:最优方案是偷第 2 4 个房间      

示例2
输入:[1,3,6]
返回值:6
说明:由于 1 和 3 是相邻的,因此最优方案是偷第 3 个房间
动态规划
	1 从1-(n-1)
	2 从2-n
计算两边动态规划
import java.util. ;

public class Solution {
    public int rob (int[] nums) {
        int[][] dp=new int[nums.length][2];
        dp[0][0]=0;
        dp[0][1]=0;
        
        int RobFirst=0;
        for(int i=1;i<nums.length;i++){
            dp[i][0]=dp[i-1][1]+nums[i-1];
            dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]);
            RobFirst=Math.max(dp[i][0],dp[i][1]);
        }
        
        int noRobFirst=0;
        dp=new int[nums.length][2];
        for(int i=1;i<nums.length;i++){
            dp[i][0]=dp[i-1][1]+nums[i];
            dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]);
            noRobFirst=Math.max(dp[i][0],dp[i][1]);
        }
        return Math.max(RobFirst,noRobFirst);
    }
}

BM80 买卖股票的最好时机(一)

描述
假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天
2.如果不能获取到任何利润,请返回0
3.假设买入卖出均无手续费
数据范围:0≤n≤10^5,0≤val≤10^4
要求:空间复杂度 O(1),时间复杂度 O(n)

示例1
输入:[8,9,2,5,4,7,1]
返回值:5
说明:在第3天(股票价格 = 2)的时候买入,在第6天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第2天买入,第3天卖出,这样就亏损7了;同时,你也不能在买入前卖出股票。            

示例2
输入:[2,4,1]
返回值:2

示例3
输入:[3,2,1]
返回值:0
思路:
	动态规划
	今天持有=max(今天买入,昨天就持有)
	今天不持有=max(昨天没有,昨天有今天卖出)
import java.util. ;
public class Solution {
    public int maxProfit (int[] prices) {
        int[][] dp=new int[prices.length][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=Math.max(dp[i - 1][1], -prices[i]);
        }
        return dp[prices.length-1][0];
    }
}
//暴力解法
import java.util. ;
public class Solution {
    public int maxProfit (int[] prices) {
        if(prices.length<2) return 0;
        int max=0;
        for(int i=0;i<prices.length-1;i++){
            int n1=prices[i];
            for(int j=i+1;j<prices.length;j++){
                max=Math.max(max,prices[j]-n1);
            }
        }
        return max;
    }
}

BM81 买卖股票的最好时机(二)

描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费
数据范围:1≤n≤1×10^5,1≤prices[i]≤10^4
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)

示例1
输入:[8,9,2,5,4,7,1]
返回值:7
说明:
在第1天(股票价格=8)买入,第2天(股票价格=9)卖出,获利9-8=1
在第3天(股票价格=2)买入,第4天(股票价格=5)卖出,获利5-2=3
在第5天(股票价格=4)买入,第6天(股票价格=7)卖出,获利7-4=3
总获利1+3+3=7,返回7        

示例2
输入:[5,4,3,2,1]
返回值:0
说明:由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。            

示例3
输入:[1,2,3,4,5]
返回值:4
说明:第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。                
备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
思路:动态规划
	今天持有=max(昨天利润+今天买入,昨天就持有)
	今天不持有=max(昨天没有,昨天有今天卖出)
import java.util. ;
public class Solution {
    public int maxProfit (int[] prices) {
        int[][] dp=new int[prices.length][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=Math.max(dp[i - 1][1],dp[i-1][0] -prices[i]);
        }
        return dp[prices.length-1][0];
    }
}

BM82 买卖股票的最好时机(三)

描述
假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费
数据范围:1≤n≤10^5,股票的价格满足1≤val≤10^4
要求: 空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)

示例1
输入:[8,9,3,5,1,3]
返回值:4
说明:
第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2
第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2
总收益为4。              

示例2
输入:[9,8,4,1]
返回值:0

示例3
输入:[1,2,8,3,8]
返回值:12

说明:第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。
因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。        
备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。
思路:
	5个状态:1)不操作2)第一次购买3)第一次卖出4)第二次购买5)第二次卖出
import java.util. ;

public class Solution {
    public int maxProfit (int[] prices) {
        // write code here
        if (prices.length == 0) return 0;
        / 
        5个状态:1)不操作2)第一次购买3)第一次卖出4)第二次购买5)第二次卖出
        dp[i][j]代表第i天状态为j时产生的最大收益
         /
        int [][]dp = new int[prices.length][5];
        //初始化
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for (int i = 1; i<prices.length; i++) {
            dp[i][0] = dp[i - 1][0];
            //其中dp[i][1]有两个操作1)第i天没有操作2)第i天买入股票,所以此时最大收益,应该为这两个操作比大小
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            //其中dp[i][2]有两个操作1)第i天没有操作2)第i天卖出股票,所以此时最大收益,应该为这两个操作比大小
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
            //其中dp[i][3]有两个操作1)第i天没有操作2)第i天买入股票,所以此时最大收益,应该为这两个操作比大小
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
            //其中dp[i][4]有两个操作1)第i天没有操作2)第i天卖出股票,所以此时最大收益,应该为这两个操作比大小
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
        }
        return dp[prices.length - 1][4];

    }
}
举报

相关推荐

0 条评论