美文网首页动态规划
Leetcode-Java(十四)

Leetcode-Java(十四)

作者: 文哥的学习日记 | 来源:发表于2018-03-27 14:23 被阅读49次

131. Palindrome Partitioning

回溯法

class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<List<String>>();
        if(s==null || s.length()==0)
            return res;
        backtracking(s,0,res,new ArrayList<String>());
        return res;
    }
    
    public void backtracking(String s,int start,List<List<String>> res,List<String> arr){
        if(arr.size() > 0 && start==s.length()){
            res.add(new ArrayList<String>(arr));
            return;
        }
        for(int i=start;i<s.length();i++){
            if(isPalindrome(s,start,i)){
                arr.add(s.substring(start,i+1));
                backtracking(s,i+1,res,arr);
                arr.remove(arr.size()-1);
            }
        }
    }
    public boolean isPalindrome(String s,int start,int end){
        while(start<=end ){
            if(s.charAt(start)==s.charAt(end)){
                start++;
                end--;
            }
            else{
                return false;
            }
        }
        return true;
        
    }
}

132. Palindrome Partitioning II

对于输入字符串如s=“aab”,一个直观的思路是判断“aab”是否构成回文,根据回文的特点是对称性,那么,我们可以判断s[0]与s[2]是否相等,不等,因此“aab”不能构成回文,此后再怎么判断呢???这种无章法的操作没有意义,因为一个字符串构成回文的情况是很复杂的,对于一个长度为n的字符串,其构成回文的子串长度可能的长度分布范围可以是1—n:整体构成回文如“baab”,则子串长度可为n=4;如“cab”,只能构成长度为1的回文子串。

鉴于上述分析,对于一个字符串,我们需要考虑所有可能的分割,这个问题可以抽象成一个DP问题,对于一个长度为n的字符串,设DP[i][j]表示第i个字符到第j个字符是否构成回文,若是,则DP[i][j]=1;若否,则DP[i][j]=0;如此,根据回文的约束条件(对称性),DP[i][j]构成回文需满足:

1、输入字符串s[i]==s[j],对称性;
2、条件1满足并不能保证i到j构成回文,还须:(j-i)<=1或者DP[i+1][j-1]=1;即,i、j相邻或者i=j,也就是相邻字符相等构成回文或者字符自身构成回文,如果i、j不相邻或者相等,i到j构成回文的前提就是DP[i+1][j-1]=1.

所以状态转移方程:DP[i][j]=(s[i]==s[j]&&(j-i<=1||DP[i+1][j-1]==1))。由于i必须小于j,i>=0&&i<s.length().如此,DP[i][j]构成一个下三角矩阵,空间、时间复杂度均为O(n2),如下所示:对于长度为4的字符串s=“baab”,其中红色部分为i>j,为无意义部分;绿色部分i==j,即字符串自身必然构成回文,DP[i][j]=1;白色部分,为长度大于1的子串,需要状态转移方程进行判断。

经过判断,得到状态矩阵:绿色部分,即字符串“baab”可构成的回文子串分割情况:绿色部分DP[0][0]、DP[1][1]、DP[2][2]和DP[3][3]构成的是单字符回文子串,DP[1][2]和DP[0][3]构成的是多字符回文子串。

在得到输入字符串的所有回文子串的分割情况之后,我们需要考虑如何求取回文子串的最小分割次数,显然,回文子串越长,分割次数越少,因此,分割的时候回文子串应分别取最大长度,如上面的例子,s="baab",可行的分割情况有三种:(显然,最少分割次数为0,当然,根据DP[][]矩阵可以很容易求取最长回文子串!!!!)。

1、{DP[0][0]、DP[1][1]、DP[2][2]、DP[3][3]};
2、{DP[0][0]、DP[1][2]、DP[3][3]};
3、{DP[0][3]}

当输入字符串所有可能的分割情况求出来之后,我们需要进一步寻找最少分割次数,我们可以用一个一维数组来存储分割次数:设int[] cut=new int[s.length()+1],cut[i]表示第i个字符到最后一个字符所构成的子串的最小分割次数,这里的i有约束条件,就是第i个位置必须是可进行回文分割的,即DP[i][j]==1 (j>=i&&j<s.length()),故:

根据这个公式,我们最终要求的cut[0]与cut[0]、cut[1]...cut[len]都有关,直接求需要递归,效率低,因此我们可以逆序求解,即:先求cut[len-1],最后求cut[0].

class Solution {
    public int minCut(String s) {  
        int [][] dp=new int[s.length()][s.length()];  
        int [] cut=new int[s.length()+1];  
        cut[s.length()] = 0;
        for(int i=s.length()-1;i>=0;i--)  
        {  
            cut[i]=Integer.MAX_VALUE;  
            for(int j=i;j<s.length();j++)  
            {  
                if(s.charAt(i)==s.charAt(j)&&(j-i<=1||dp[i+1][j-1]==1))  
                {  
                    dp[i][j]=1;  
                    cut[i]=Math.min(1+cut[j+1],cut[i]);    
                }  
            }  
        }  
        return cut[0]-1;    
    }  
}

134. Gas Station

首先,总的油要比总的消费多,同时要保证每一个站点的时候,油都要比消费多,如果不行的话,我们要从下一个站点作为起点进行尝试,从前面的站点作为起点已经不合适了。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int sumGas = 0;
        int sumCost = 0;
        int start = 0;
        int tank = 0;
        for (int i = 0; i < gas.length; i++) {
            sumGas += gas[I];
            sumCost += cost[I];
            tank += gas[i] - cost[I];
            if (tank < 0) {
                start = i + 1;
                tank = 0;
            }
        }
        if (sumGas < sumCost) {
            return -1;
        } else {
            return start;
        }
    }
}

135. Candy

这是我的思路,从左到右判断需要多少个糖果,如果当前位置的比重大于前一个位置的,那么就是前一个位置的糖果+1,如果相等,直接为1,如果小于前面位置的权重,那么如果前面的糖果为1,则需要作出调整,如果是大于,则直接赋值1.

class Solution {
    public int candy(int[] ratings) {
        int[] res = new int[ratings.length];
        if(ratings == null || ratings.length==0)
            return 0;
        for(int i=0;i<ratings.length;i++){
            res[i]=1;
        }
        for(int i=1;i<res.length;i++){
            if(ratings[i] > ratings[i-1])
                res[i] = res[i-1] + 1;
            else if(ratings[i]==ratings[i-1])
                res[i] = 1;
            else{
                if(res[i-1] > 1)
                    res[i] = 1;
                else{
                    res[i-1] += 1;
                    int t = i-1;
                    while(t>0 && res[t]==res[t-1] && ratings[t-1] > ratings[t]){
                        res[t-1] ++;
                        t--;
                    }
                }
            }
        }
        int sum=0;
        for(int i=0;i<res.length;i++){
            sum += res[I];
        }
        return sum;
    }
}

可是上面做法超时了:

正确解法1

两个数组,一个数组只考虑左边的邻居,一个数组只考虑右边的邻居,最后两个数组对应位置中较大的数作为该位置的需要的糖果数。

    public int candy(int[] ratings) {
        if(ratings==null || ratings.length==0)
            return 0;
        int[] left2right = new int[ratings.length];
        int[] right2left = new int[ratings.length];
        for(int i=0;i<ratings.length;i++){
            left2right[i] = 1;
            right2left[i] = 1;
        }
        for(int i=1;i<ratings.length;i++){
            left2right[i] = ratings[i] > ratings[i-1] ? left2right[i-1]+1:1;
            right2left[ratings.length-i-1] = ratings[ratings.length-i-1] > ratings[ratings.length-i] ? right2left[ratings.length-i] + 1:1;
        }
        int sum = 0;
        for(int i=0;i<ratings.length;i++){
            sum += Math.max(left2right[i],right2left[I]);
        }
        return sum;
    }
}

正确解法2

只用一个数组,先从左往右遍历,再从右往左遍历,这种方式其实和第一种解法是一样的,但是只用到了一个数组,空间复杂度比较低。

public class Solution {
    public int candy(int[] ratings) {
        int[] candies = new int[ratings.length];
        Arrays.fill(candies, 1);
        for (int i = 1; i < ratings.length; i++) {
            if (ratings[i] > ratings[i - 1]) {
                candies[i] = candies[i - 1] + 1;
            }
        }
        int sum = candies[ratings.length - 1];
        for (int i = ratings.length - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candies[i] = Math.max(candies[i], candies[i + 1] + 1);
            }
            sum += candies[I];
        }
        return sum;
    }
}

136. Single Number

利用两个数的异或是0,遍历一边数组即可.

class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for(int num:nums){
            res ^= num;
        }
        return res;
    }
}

137. Single Number II

借鉴136题的思路,我们把二进制的问题转换为三进制来求解,二进制中,异或是两个一样的为0,那么在三进制中,我们设计的是三个一样的为0.

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0;
        int twos = 0;
        int threes = 0;
        for(int num:nums){
            twos |= ones & num;
            ones = ones ^ num;
            threes = ones & twos;
            ones &= ~threes;
            twos &= ~threes;
        }
        return ones;
    }
    
}

139. Word Break

回溯法:超时

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        if(s==null || s.length()==0 || wordDict==null || wordDict.size()==0)
            return false;
        boolean res = search(s,wordDict,0);
        return res;
    }
    
    public boolean search(String s,List<String> wordDict,int start){
        if(start == s.length())
            return true;
        for(int i=start;i<s.length();i++){
            if(wordDict.contains(s.substring(start,i+1))){
                boolean res = search(s,wordDict,i+1);
                if(res)
                    return true;
            }
        }
        return false;
    }
}

动态规划

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        if(s==null && wordDict==null)
            return true;
        if(s==null || wordDict==null)
            return false;
        boolean[] dp = new boolean[s.length()+1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                //如果dp[j]为true且字典中有j-i的子串的话,为true
                if (dp[j] && wordDict.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

140. Word Break II

回溯,超时

class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        List<String> res = new ArrayList<String>();
        if(s==null || wordDict==null)
            return res;
        
        search(res,s,wordDict,new StringBuilder(),0);
        return res;
    }
    
    public void search(List<String> res,String s,List<String> wordDict,StringBuilder str,int start){
        if(start == s.length()){
            res.add(new StringBuilder(str.delete(0,1).toString()).toString());
            return;
        }
        for(int i=start;i<s.length();i++){
            if(wordDict.contains(s.substring(start,i+1))){
                StringBuilder newstr = new StringBuilder(str.toString());
                newstr.append(" ");
                newstr.append(s.substring(start,i+1));
                search(res,s,wordDict,newstr,i+1);
            }
        }
    }
}

相关文章

  • Leetcode-Java(十四)

    131. Palindrome Partitioning 回溯法 132. Palindrome Partitio...

  • Leetcode-Java(二十四)

    231. Power of Two 232. Implement Queue using Stacks 题目中要求...

  • leetcode 100

    github个人所有的leetcode题解,都在我的 leetcode-java,欢迎star和共同探讨。leet...

  • Leetcode-Java(三十)

    299. Bulls and Cows 一开始我用的是HashSet保存两个字符串中出现过的数字但是没有匹配上的,...

  • Leetcode-Java(三十一)

    303. Range Sum Query - Immutable 用一个数组保存从0到当前位置的和。 304. R...

  • Leetcode-Java(二十二)

    211. Add and Search Word - Data structure design 建立一棵字典树,...

  • Leetcode-Java(二十三)

    221. Maximal Square 动态规划,只用一个一维数组,这里要注意代码里的对应关系,相当于在原数组的基...

  • Leetcode-Java(二十五)

    241. Different Ways to Add Parentheses 采用分治算法,分治算法的基本思想是将...

  • Leetcode-Java(二十六)

    257. Binary Tree Paths 类似于分治法吧。 258. Add Digits 小trick 26...

  • Leetcode-Java(二十七)

    263. Ugly Number 264. Ugly Number II 分析:这道题最直观地想法是暴力查找,但不...

网友评论

    本文标题:Leetcode-Java(十四)

    本文链接:https://www.haomeiwen.com/subject/kljqcftx.html