LeetCode:動態規劃+貪心題目整理

tribe-795發表於2018-10-24

     以下均為AC程式碼,後續將不斷更新……

 

1.字串分割:(LeetCode:word-break)

 

        Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

        For example, given  s ="leetcode",    dict =["leet", "code"]. Return true because"leetcode"can be segmented as"leet code".

        題意:判斷字串能夠分割為有集合中的幾個詞彙組成。

        題解: 動態規劃,狀態visited(i):表示以i結尾的是否能夠滿足條件,每次對從0-i的字元進行判斷。

import java.util.*;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        char [] list = s.toCharArray();
        boolean [] visited = new boolean[list.length+1];
        visited[0] = true;
        for(int i =1;i<=list.length;i++){
            for(int j = 0 ;j< i;j++){
                if(visited[j] && dict.contains(s.substring(j,i))){
                    visited[i] = true;
                    break;
                }
            }
        }
     return visited[list.length];
    }
}

 

2.字串分割II(LeetCode:word-break-ii)

        Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.Return all such possible sentences.

         For example, given   s ="catsanddog",     dict =["cat", "cats", "and", "sand", "dog"]. A solution is["cats and dog", "cat sand dog"].

        題意:字串分割後組成的句子的所有組合。

        題解:使用DFS,從頭部開始會超時,因此,從尾部開始,進行DFS,最後新增時去掉尾部的空格。

import java.util.*;
public class Solution {
    ArrayList<String> result = new ArrayList<String>(); 
     public void dfs(String s,int index, Set<String> dict,String cur) {
        if(index<=0){
            if(cur.length()>0)
                result.add(cur.substring(0,cur.length()-1));
        }
        for(int i = index;i>=0;i--){
            if(dict.contains(s.substring(i,index))){
                dfs(s,i,dict,s.substring(i,index)+" "+cur);
            }
        }
    }

    public ArrayList<String> wordBreak(String s, Set<String> dict) {
        dfs(s,s.length(),dict,"");
        return result;
    }
}

 

3.分糖果(LeetCode:candy):

      There are N children standing in a line. Each child is assigned a rating value.You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

      What is the minimum candies you must give?

      題意:每個小朋友都有一顆糖,節點高的同學比旁邊的孩子多拿糖,求最少的糖個數。

       題解:遍歷兩邊,首先每個人得一塊糖,第一遍從左到右,若當前點比前一個點高就比前者多一塊。 這樣保證了在一個方向上滿足了要求。第二遍從右往左,若左右兩點,左側高於右側,但左側的糖果數不多於右側,則左側糖果數等於右側糖果數+1,這就保證了另一個方向上滿足要求

public class Solution {
    public int candy(int[] ratings) {
        int dp[] = new int[ratings.length];
        if(ratings.length==1)
            return 1;
        for(int i =0;i<ratings.length;i++){
            dp[i]= 1;
        }
        for(int i =1;i<ratings.length;i++){
            if(ratings[i]>ratings[i-1]  )
                dp[i]= dp[i-1]+1;
        }
        int count =dp[ratings.length-1];
        for(int i =ratings.length-2;i>=0;i--){
            if(ratings[i]>ratings[i+1] && dp[i]<=dp[i+1] )
                dp[i]= dp[i+1]+1;
            count+=dp[i];
        }
        return count;
    }
}

 

4.加油站(LeetCode:gas-station)

    There are N gas stations along a circular route, where the amount of gas at station i isgas[i]. You have a car with an unlimited gas tank and it costscost[i]of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.

Note:
      The solution is guaranteed to be unique.

       題意:環形路線上有N個加油站,每個加油站有汽油gas[i],從每個加油站到下一站消耗汽油cost[i],問從哪個加油站出發能夠回到起始點,如果都不能則返回-1(注意,解是唯一的)。

        題解:total用來判斷是否有解,total<0表示沒有解,同理,從i點出發,如果remains<0,表示無法到達該點,應該從下一個點開始重新計算,因為第一個點開始時remains>0,當小於0時,中間的點也無法到達該點。

public class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int index = -1;
        int total =0;
        int dif =0;
        int remains = 0;
        for(int i=0;i<gas.length;i++){
           dif = gas[i]-cost[i];
           remains +=dif;
           total +=dif;
           if(remains<0){
               index = i;
               remains=0;
           }
        }
        if(total<0)
            return -1;
        else
            return index+1;
    }
}

 

5.三角形求最小路徑和(LeetCode:triangle)

        Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

       The minimum path sum from top to bottom is11(i.e., 2 + 3 + 5 + 1 = 11).

Note:
        Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

       題解:自底向上,先將最後一層的數字賦值給dp陣列,然後自底向上,每一層的節點和下一層的兩個節點進行比較,得到最小值,最後輸出dp[0],即為最小值。

import java.util.*;
public class Solution {
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        ArrayList<Integer> list = triangle.get(triangle.size()-1);
        int len = list.size();
        int dp[] = new int[len];
       for(int i =0;i<len;i++){
            dp[i] = list.get(i);
        }
        for(int i =triangle.size()-2;i>=0;i--){
            int temp = triangle.get(i).size();
            for(int j =0;j<temp;j++){
                    dp[j] = Math.min(triangle.get(i).get(j)+ dp[j+1] 
                                     ,triangle.get(i).get(j)+ dp[j]);
            }
        }
        return dp[0];
     }
}

 

6.不同子序列(LeetCode:distinct-subsequences)

       Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie,"ACE"is a subsequence of"ABCDE"while"AEC"is not).

Here is an example:   S ="rabbbit", T ="rabbit"    Return3.

      題意:給定兩個字串,選擇只可以用刪除字元的方法從第一個字串變換到第二個字串,求出一共有多少種變換方法;

      題解:定義二維陣列dp[i][j]為字串s(0,i)變換到t(0,j)的變換方法。如果S[i]==T[j],那麼dp[i][j] = dp[i-1][j-1] + dp[i-1][j]。意思是:如果當前S[i]==T[j],那麼當前這個字母即可以保留也可以拋棄。如果S[i]!=T[i],那麼dp[i][j] = dp[i-1][j],意思是如果當前字元不等,那麼就只能拋棄當前這個字元。遞迴公式中用到的dp[0][0] = 1,dp[i][0] = 0(把任意一個字串變換為一個空串只有一個方法)

public class Solution {
    public int numDistinct(String S, String T) {
        int n = S.length();
        int m = T.length();
        int [][]dp = new int[m+1][n+1];
        dp[0][0]=1;
        for(int i =1;i<=m;i++){
            dp[i][0] = 0;
        }
        for(int j =1;j<=n;j++){
            dp[0][j] = 1;
        }
        for(int i =1;i<=m;i++){
            for(int j =1;j<=n;j++){
                if(S.charAt(j-1)== T.charAt(i-1))
                    dp[i][j] = dp[i-1][j-1]+dp[i][j-1];
                else
                    dp[i][j] = dp[i][j-1];
            }
        }
        return dp[m][n];
    }
}

 

7.判斷字串是否由兩個子串拼接而成:(LeetCode:interleaving-string)

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,
Given:
    s1 ="aabcc",
     s2 ="dbbca",

When s3 ="aadbbcbcac", return true.
When s3 ="aadbbbaccc", return false.

     題意:s3是由s1和s2交織生成的,意味著s3由s1和s2組成,在s3中s1和s2字元的順序是不能變化的,和子序列題型類似,這種題我們一般是用動態規劃來解。

     題解: 設dp[i][j]表示s3的前i+j個字元可以由s1的前i個字元和s2的前j個字元交織而成。狀態轉移方程:有兩種情況 :

               dp[i][j]= {(dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)}

              dp[i][j]= {(dp[i][j-1] && s2.charAt(j - 1) == s3.charAt(i + j - 1)}

  分別表示第i+j個字元由s1構成或者由S2組成。

public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1.length() + s2.length() != s3.length())
            return false;
        if(s1==null )
            return s2.equals(s3);
        if(s2==null)
            return s1.equals(s3);
        //dp[i][j]代表 s1[0...i]  s2[0...j]能否順序匹配 s3[i+j-1]
        boolean dp[][] = new boolean[s1.length()+1][s2.length()+1];
         dp[0][0] = true;
        //初始化邊界值
        for(int i =1;i<=s1.length();i++){
            dp[i][0] = dp[i-1][0] && s1.charAt(i-1)==s3.charAt(i-1);
        }
        for(int j =1;j<=s2.length();j++){
            dp[0][j] = dp[0][j-1] && s2.charAt(j-1)==s3.charAt(j-1);    
        }
         for(int i =1;i<=s1.length();i++){
             for(int j =1;j<=s2.length();j++){
                dp[i][j] = (dp[i][j-1] && s2.charAt(j-1)==s3.charAt(i+j-1))
                        || (dp[i-1][j] && s1.charAt(i-1)==s3.charAt(i+j-1));       
            }
        }       
       return dp[s1.length()][s2.length()];
    }
  }

 

8.  數字轉字串(LeetCode:decode-ways)

       A message containing letters fromA-Zis being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

       Given an encoded message containing digits, determine the total number of ways to decode it. For example, Given encoded message"12", it could be decoded as"AB"(1 2) or"L"(12). The number of ways decoding"12"is 2.

       題解:動態規劃。類似於劍指offer中的數字轉字串,區別在於從0開始和從1開始。

public class Solution {
    public int numDecodings(String s) {
        char list[] = s.toCharArray();
        if(list.length==0 || list[0]=='0')
            return 0;
        if(list.length==1)
            return 1;
        int [] dp = new int[list.length+1];
        dp[0]=1;
        if(list[0]=='0')
            dp[1]=0;
        else
            dp[1]=1;        
        for(int i = 2;i<=list.length;i++){
            if((list[i-1]-'0')!=0)
                 dp[i] = dp[i-1];
            else
                dp[i] = 0;
            if((list[i-2]-'0')==1 ||
                     ((list[i-2]-'0')==2 && (list[i-1]-'0')<=6))
                dp[i] += dp[i-2];

        }
        return dp[list.length];
    }
}

 

9.  格雷碼(LeetCode:gray-code)

       The gray code is a binary numeral system where two successive values differ in only one bit.Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.For example, given n = 2, return[0,1,3,2]. Its gray code sequence is:

00 - 0
01 - 1
11 - 3
10 - 2

Note:
     For a given n, a gray code sequence is not uniquely defined.For example,[0,2,3,1]is also a valid gray code sequence according to the above definition.For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

       題解:隨著n變大,前面的數不用動,後面的數倒著拿出來再在首部加1即可。

import java.util.*;
public class Solution {
    ArrayList<Integer> result =  new ArrayList<Integer>();
    public ArrayList<Integer> grayCode(int n) {
        result.add(0);
        if(n==0)
            return result;
        result.add(1);
        if(n==1)
             return result;
        for(int i =2;i<=n;i++){
            int size = result.size();
            for(int j =size-1;j>=0;j--){
                result.add(result.get(j)+(int)Math.pow(2,i-1));
            }
        }
        return result;
    }
}

 

10.直方圖中最大的矩形(LeetCode:largest-rectangle-in-histogram)    

        Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height =[2,1,5,6,2,3].

 

        The largest rectangle is shown in the shaded area, which has area =10unit.

         For example,Given height =[2,1,5,6,2,3],return10.

       題解:利用單調棧來實現,棧中儲存的是索引的位置,滿足遞增時,將元素壓入棧中,不滿足時依次彈出,直到滿足,彈出的過程計算以彈出元素為高度時的最大面積。

import java.util.*;
public class Solution {
    public int largestRectangleArea(int[] height) {
        if(height.length==1)
            return height[0];
        Stack<Integer> s = new Stack<Integer>();
        int cur = 0;
        int max = 0;
        int len =0;
        for(int i =0;i<height.length;i++){
            if(s.isEmpty() || height[s.peek()]<=height[i]){
                s.push(i);
            }else{
                while(!s.isEmpty() && height[s.peek()]>height[i]){
                    int index = s.pop();
                    if(s.isEmpty())//刪除一個元素後為空
                        len = i;
                    else
                        len = i-s.peek()-1;
                    max = Math.max(max,len*height[index]);
                }
                s.push(i);
             }
        }
       while(!s.isEmpty()){
            int index = s.pop();
            if(s.isEmpty())//刪除一個元素後為空
                 len = height.length;
            else
                 len = height.length-s.peek()-1;
            max = Math.max(max,len*height[index]);
       }
        return max;
    }
}

 

11.矩陣中的最大全1矩形面積:(LeetCode:maximal-rectangle)

      Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

     題解:將矩形按照列進行統計轉化,然後將每一行呼叫上一題中的計算直方圖中的最大面積方法,得到最大值,最後找到整體的最大值。

import java.util.*;
public class Solution {
    public int maximalRectangle(char[][] matrix) {
        if(matrix.length==0 || matrix==null)
            return 0;
        int m = matrix.length;
        int n = matrix[0].length;
        int [][] temp = new int[m][n];
        int max =0;
        int result =0;
       for(int j =0;j<n;j++){
            for(int i =0;i<m;i++){
                if(matrix[i][j] == '1'){
                    if(i==0)
                       temp[i][j] = matrix[i][j]-'0';
                    else
                       temp[i][j] = temp[i-1][j] + 1;     
                }else{
                    temp[i][j] =0;
                }
            }
        }
        for(int i =0;i<m;i++){
            result = Rectangle(temp[i]);
            max = Math.max(max,result);
        }
        return max;
    }
    
     public int Rectangle(int[] temp) {
         if(temp.length==1)
             return temp[0];
          int len =0;
          int max =0;
          Stack<Integer> s = new Stack<Integer>();
          for(int k =0;k<temp.length;k++){
                if(s.isEmpty() || temp[k]>=temp[s.peek()]){
                    s.push(k);
                }else{
                    while(!s.isEmpty() && temp[k]<temp[s.peek()]){
                        int index = s.pop();
                        if(s.isEmpty())
                            len = k;
                        else
                            len = k-s.peek()-1;
                        max = Math.max(max,temp[index]*len);
                    }
                    s.push(k);
                }
            }
            while(!s.isEmpty()){
                 int index = s.pop();
                 if(s.isEmpty())
                     len = temp.length;
                 else
                     len =temp.length-s.peek()-1;
                 max = Math.max(max,temp[index]*len);
            }
         return max;
        }
    
}

 

12.編輯距離(LeetCode:edit-distance)

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)You have the following 3 operations permitted on a word:

a) Insert a character
b) Delete a character
c) Replace a character

     題意:從一個字串轉變到另一個字串需要的變換步驟,共有三種變換方式,插入一個字元,刪除一個字元,和替換一個字元。

     題解:設定二維動態陣列,初始邊界的值,迴圈遍歷時,如果兩個字串所指的字元相等,則等於dp[i-1][j-1],如果不相等,因為有三種操作,刪除dp[i][j-1]、插入dp[i-1][j]、替換dp[i-1][j-1],從中選擇最小的值進行加1;

public class Solution {
    public int minDistance(String word1, String word2) {
        // dp[i][j]代表由word1的前i個子串變為word2的前j個子串的花費
        int len1 = word1.length();
        int len2 = word2.length();
        int dp[][] = new int[len1+1][len2+1];
        for(int i =0;i<=len1;i++){
            dp[i][0] = i;
        }
        for(int j =0;j<=len2;j++){
            dp[0][j] = j;
        }
        for(int i =1;i<=len1;i++){
            for(int j =1;j<=len2;j++){
                if(word1.charAt(i-1)==word2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1];
                else
                    dp[i][j] =  Math.min(dp[i-1][j-1],
                                         Math.min(dp[i][j-1],dp[i-1][j]))+1;
            }
        }
        return dp[len1][len2];
    }
}

 

13.最小路徑和(LeetCode:minimum-path-sum):

       Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

      題意:從矩陣的左上角到右下角的最小路徑和。

      題解:設定二維動態陣列來儲存當前點的最小值,對每一行及每一列進行初始化,然後開始遍歷,最小值為左和上的最小值+當前值。

public class Solution {
    public int minPathSum(int[][] grid) {
        int row = grid.length;
        int col = grid[0].length;
        int[][] sum = new int[row][col];
        sum[0][0]= grid[0][0];
        for(int i = 1;i<row;i++)
            sum[i][0] = sum[i-1][0]+grid[i][0];
        for(int j = 1;j<col;j++)
            sum[0][j] = sum[0][j-1]+grid[0][j]; 
        for(int i = 1;i<row;i++){
            for(int j = 1;j<col;j++){
                sum[i][j] = Math.min(sum[i-1][j],sum[i][j-1])+grid[i][j];
            }
        }
        return sum[row-1][col-1];
    }
}

14.機器人走方格(LeetCode:unique-paths):  

      A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).How many possible unique paths are there?

     題意:每次只能向下或者向右走,求路線總數。

      題解:設定二維動態陣列,初始邊界的值,為1,然後迴圈判斷每個點的路線數,當前點的路線數=左邊點的路線數+上邊點的路線數。

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

 

15.機器人走方格(LeetCode:unique-paths-ii):  

     題意:方格中考慮障礙, 每次只能向下或者向右走,求路線總數。

     題解:設定二維動態陣列,初始邊界的值,如果沒有障礙,設為1,然後迴圈判斷每個點的路線數,沒有障礙時,當前點的路線數=左邊點的路線數+上邊點的路線數。

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int row = obstacleGrid.length;
        int col = obstacleGrid[0].length;
        int dp[][] = new int[row+1][col+1];
        for(int i =0;i<row;i++){
            if(obstacleGrid[i][0]==0)
                dp[i][0]=1;
            else
                break;
        }
         for(int j =0;j<col;j++){
             if(obstacleGrid[0][j]==0)
                dp[0][j] = 1;
            else
               break;
         }
        for(int i =1;i<row;i++){
             for(int j =1;j<col;j++){
                 if(obstacleGrid[i][j]==0)
                     dp[i][j] = dp[i-1][j] + dp[i][j-1];
                 else
                     dp[i][j] =0;
             }
        }
        return dp[row-1][col-1];
    }
}

 

16.跳躍問題(LeetCode:jump-game)

       Given an array of non-negative integers, you are initially positioned at the first index of the array.Each element in the array represents your maximum jump length at that position.Determine if you are able to reach the last index.

        For example:    A =[2,3,1,1,4], return   true.     A =[3,2,1,0,4], return   false.

       題意:已知陣列,值表示最大跳躍長度,求是否可以達到終點。

        題解:採用貪心演算法來解決,但需要保證該點可以到達,使用i<=max保證。

public class Solution {
    public boolean canJump(int[] A) {
        //貪心演算法:
        if(A.length==1)
            return true;
        int max=0;
        //i<=max保證該點可以到達
        for(int i =0;i<A.length && i<=max;i++){
            int temp = A[i];
             if(i+temp>max)
                max=i+temp;
             if(max>=A.length-1)
                 return true;
        }
       return false;
    }
}

 

17.跳躍問題(LeetCode:jump-game-ii)

       Given an array of non-negative integers, you are initially positioned at the first index of the array.Each element in the array represents your maximum jump length at that position.Your goal is to reach the last index in the minimum number of jumps.

        For example:   Given array A =[2,3,1,1,4]。 The minimum number of jumps to reach the last index is2. (Jump1step from index 0 to 1, then3steps to the last index.)

     題意:已知陣列,值表示最大跳躍長度,求最少跳躍次數。

     題解: 動態規劃,dp[i]放置到達i點的最少步數,首先都賦值為最大,每一次進行比較,取最小值進行儲存。

public class Solution {
    public int jump(int[] A) {
        int len = A.length;
        int [] dp = new int[len+1];
        for(int i =0;i<=len;i++)
            dp[i] = Integer.MAX_VALUE;
        dp[0]=0;
        for(int i =0;i<len;i++){
            int temp = A[i];
            for(int j =0;j<=temp;j++){
                if(i+j>len)
                    break;
                dp[i+j] = Math.min(dp[i+j],dp[i]+1);
            }
        }
        return dp[len-1];
    }
}

 

相關文章