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”是否构成回文,根据回文的特点是对称性,那么,我们可以判断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);
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容