Leetcode-Java(十四)

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”是否構(gòu)成回文,根據(jù)回文的特點是對稱性,那么,我們可以判斷s[0]與s[2]是否相等,不等,因此“aab”不能構(gòu)成回文,此后再怎么判斷呢???這種無章法的操作沒有意義,因為一個字符串構(gòu)成回文的情況是很復(fù)雜的,對于一個長度為n的字符串,其構(gòu)成回文的子串長度可能的長度分布范圍可以是1—n:整體構(gòu)成回文如“baab”,則子串長度可為n=4;如“cab”,只能構(gòu)成長度為1的回文子串。

鑒于上述分析,對于一個字符串,我們需要考慮所有可能的分割,這個問題可以抽象成一個DP問題,對于一個長度為n的字符串,設(shè)DP[i][j]表示第i個字符到第j個字符是否構(gòu)成回文,若是,則DP[i][j]=1;若否,則DP[i][j]=0;如此,根據(jù)回文的約束條件(對稱性),DP[i][j]構(gòu)成回文需滿足:

1、輸入字符串s[i]==s[j],對稱性;
2、條件1滿足并不能保證i到j(luò)構(gòu)成回文,還須:(j-i)<=1或者DP[i+1][j-1]=1;即,i、j相鄰或者i=j,也就是相鄰字符相等構(gòu)成回文或者字符自身構(gòu)成回文,如果i、j不相鄰或者相等,i到j(luò)構(gòu)成回文的前提就是DP[i+1][j-1]=1.

所以狀態(tài)轉(zhuǎn)移方程: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]構(gòu)成一個下三角矩陣,空間、時間復(fù)雜度均為O(n2),如下所示:對于長度為4的字符串s=“baab”,其中紅色部分為i>j,為無意義部分;綠色部分i==j,即字符串自身必然構(gòu)成回文,DP[i][j]=1;白色部分,為長度大于1的子串,需要狀態(tài)轉(zhuǎn)移方程進(jìn)行判斷。

經(jīng)過判斷,得到狀態(tài)矩陣:綠色部分,即字符串“baab”可構(gòu)成的回文子串分割情況:綠色部分DP[0][0]、DP[1][1]、DP[2][2]和DP[3][3]構(gòu)成的是單字符回文子串,DP[1][2]和DP[0][3]構(gòu)成的是多字符回文子串。

在得到輸入字符串的所有回文子串的分割情況之后,我們需要考慮如何求取回文子串的最小分割次數(shù),顯然,回文子串越長,分割次數(shù)越少,因此,分割的時候回文子串應(yīng)分別取最大長度,如上面的例子,s="baab",可行的分割情況有三種:(顯然,最少分割次數(shù)為0,當(dāng)然,根據(jù)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]}

當(dāng)輸入字符串所有可能的分割情況求出來之后,我們需要進(jìn)一步尋找最少分割次數(shù),我們可以用一個一維數(shù)組來存儲分割次數(shù):設(shè)int[] cut=new int[s.length()+1],cut[i]表示第i個字符到最后一個字符所構(gòu)成的子串的最小分割次數(shù),這里的i有約束條件,就是第i個位置必須是可進(jìn)行回文分割的,即DP[i][j]==1 (j>=i&&j<s.length()),故:

根據(jù)這個公式,我們最終要求的cut[0]與cut[0]、cut[1]...cut[len]都有關(guān),直接求需要遞歸,效率低,因此我們可以逆序求解,即:先求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

首先,總的油要比總的消費多,同時要保證每一個站點的時候,油都要比消費多,如果不行的話,我們要從下一個站點作為起點進(jìn)行嘗試,從前面的站點作為起點已經(jīng)不合適了。

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

這是我的思路,從左到右判斷需要多少個糖果,如果當(dāng)前位置的比重大于前一個位置的,那么就是前一個位置的糖果+1,如果相等,直接為1,如果小于前面位置的權(quán)重,那么如果前面的糖果為1,則需要作出調(diào)整,如果是大于,則直接賦值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

兩個數(shù)組,一個數(shù)組只考慮左邊的鄰居,一個數(shù)組只考慮右邊的鄰居,最后兩個數(shù)組對應(yīng)位置中較大的數(shù)作為該位置的需要的糖果數(shù)。

    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

只用一個數(shù)組,先從左往右遍歷,再從右往左遍歷,這種方式其實和第一種解法是一樣的,但是只用到了一個數(shù)組,空間復(fù)雜度比較低。

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

利用兩個數(shù)的異或是0,遍歷一邊數(shù)組即可.

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

137. Single Number II

借鑒136題的思路,我們把二進(jìn)制的問題轉(zhuǎn)換為三進(jìn)制來求解,二進(jìn)制中,異或是兩個一樣的為0,那么在三進(jìn)制中,我們設(shè)計的是三個一樣的為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;
    }
}

動態(tài)規(guī)劃

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);
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容