The String ||

挺综合的一道题目,核心方法是NP的DFS。 回头应当做俩遍
实现中需要一个判断数字是否为合法ip地址的一项的函数,首先要在0-255之间,其次前面字符不能是0。剩下的就是 NP问题 的套路了,递归中套一个for循环,不熟悉的朋友可以看看 N-Queens 哈。
--Code Ganker

//update my own version
    public List
   
   
    
     restoreIpAddresses(String s) {
         List
    
    
     
      res = new ArrayList
     
     
      
      ();
         helper(res,"",s,0,0);
         return res;
    }
    public void helper( List
      
      
       
        res, String item, String s, int start, int count){
        if(start == s.length() && count == 4){
            res.add(item);
            return;
        }
        if(count >= 4 || start == s.length())
            return;
        if(s.charAt(start) == '0'){
            String newItem = start==s.length()-1 ? item + s.substring(start, start+1) : item + s.substring(start, start+1) + ".";
            helper(res,newItem,s,start+1,count+1);
            return;
        }
        
        for(int i= start; i
       
       
         restoreIpAddresses(String s) { List 
        
          res = new ArrayList 
         
           (); if(s == null || s.length() == 0 || s.length() > 12) return res; helper(res,s,"",0,1); return res; } public void helper(List 
          
            res, String s, String temp, int index, int segment){ if(index > s.length() - 1){ return; } if(segment == 4){ String seg4 = s.substring(index); if(isValid(seg4)){ res.add(temp+"."+ seg4); } return; } for(int i=1;i<4 && index + i < s.length();i++){ String seg = s.substring(index,index+i); if(isValid(seg)){ if(segment == 1){ helper(res,s,seg,index+i,segment+1); }else{ helper(res,s,temp+"."+seg,index+i,segment+1); } } } } public boolean isValid(String str){ if(str.length() > 3) return false; if(str.charAt(0) == '0' && str.length() > 1) return false; int strInt = Integer.parseInt(str); if( strInt >= 0 && strInt <= 255) return true; return false; } } 
           
          
         
       
      
      
     
     
    
    
   
   

算法题目是很有意思的一个东西,很喜欢一句话,应该就是算法所追求的哲学 less is more 。 正确的数据结构和变量的使用十分重要,能不能找到问题的实质,而非处理纷杂的表象。Code Ganker的代码不论长短,总是能揭示实质。与其它几位大神的code相比,我个人认为是要高出一个段位点。

这也是一道brain fuck的题目,我梳理一下我使用stack时候遇到的问题。
1. stack.isEmpty()的检查等价于 count_left  这个变量点,我一开始又重复使用了这个变量来计数剩余左括号。
2. 最终承上启下的变量用的是start,我找了很久没确定对这个变量,我使用了 last_end  , origin(类似start) 这俩个变量,这里的问题是,不可以从表象与个例中着手定义变量,last_end 这种廉价的变量显然是为了折中的处理 “()()()()” 这种情况,我一开始误判为这是一个累加点过程 2+2+2+2。
3. start用的巧妙,但从start入手难理解此题真谛,这个题的核心逻辑是 stack.peek() 所揭示的。我在做的时候,int index = stack.pop(), 然后在 - stack.peek()的位置使用了- index + 1. 这也是我这道题最大的启发,说俩点 1. 有些时候 二者是相等的,有些时候不等。而stack.peek()总是包含了index-1 的情况的,所以更优。2. 之前为何要用到一个变量start?因为我们要用的是peek(), 当stack为空时,没有peek()可以call, 我们用start-1代替,实际上start-1的意义正是stack.peek()

"(()()"这种情况,必须用peek(), 用index -1 就不对了。还是说,index-1是表象的巧合,而peek()才是问题的实质。

public class Solution {
    public int longestValidParentheses(String s) {
        LinkedList
    
    
     
      stack = new LinkedList
     
     
      
      ();
        int start = 0;
        int res = 0;
        for(int i=0; i
      
      
     
     
    
    

this one is definitely the most heart fucking one ever and never. I spent 5 hours on this medium problem... And failed submission more than 50 times...
Mother Fucker...
Any thing about math trick, kills me. 
Ironically I don't even know the rule of multiplication. when use index, i for index of num1, j for index of num2, and i = 0 is the first entry of num1, if we arrange the number as one,ten,hundred,...  , then we have the following fact, for the i th entry(ith smallest, 0th entry is one, 1th entry is ten...), it comes from  mth entry from num1, and (i-m)th entry from num2 if valid.
这个乘法看起来每乘一位是进位相加,其实这个进位是自动进行的,因为第一个乘数 j 进了一位, i+j自然跟着进了一位。
FUCK...
public class Solution {
      public String multiply(String num1, String num2) {
        if(num1.charAt(0) == '0' || num2.charAt(0) == '0'){
            return "0";
        }
        StringBuilder sb1 = new StringBuilder(num1);
        StringBuilder sb2 = new StringBuilder(num2);
        String rnum1 = sb1.reverse().toString();
        String rnum2 = sb2.reverse().toString();
        int[] res = new int[num1.length() + num2.length()];
        int carry = 0;
        for(int i=0; i
    
    

像这种判断能否按照某种规则来完成求是否或者某个量的题目,很容易会想到用动态规划来实现。
先说说维护量,res[i][j]表示用s1的前i个字符和s2的前j个字符能不能按照规则表示出s3的前i+j个字符,如此最后结果就是res[s1.length()][s2.length()],判断是否为真即可。接下来就是递推式了,假设知道res[i][j]之前的所有历史信息,我们怎么得到res[i][j]。可以看出,其实只有两种方式来递推,一种是选取s1的字符作为s3新加进来的字符,另一种是选s2的字符作为新进字符。而要看看能不能选取,就是判断s1(s2)的第i(j)个字符是否与s3的i+j个字符相等。如果可以选取并且对应的res[i-1][j](res[i][j-1])也为真,就说明s3的i+j个字符可以被表示。这两种情况只要有一种成立,就说明res[i][j]为真,是一个或的关系。所以递推式可以表示成
res[i][j] = res[i-1][j]&&s1.charAt(i-1)==s3.charAt(i+j-1) || res[i][j-1]&&s2.charAt(j-1)==s3.charAt(i+j-1)
-- Code Ganker
感觉DP快到火候了,继续。

不过遇到这种题总喜欢用recursion解一下,总觉得recursion更有意思。这里用到之前用过的一种预判,如果排序后的s1+s2   不等于排序后的 s3, 那么直接返回false。但是还是TLE了 :(
public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1 == null || s2 == null)
            return false;
        if(s3.length() != s1.length() + s2.length())
            return false;
        boolean[][] dp = new boolean[s1.length()+1][s2.length()+1];
        dp[0][0] = true;
        for(int i=1;i<=s2.length();i++){
            if(s2.substring(0,i).equals(s3.substring(0,i))){
                dp[0][i] = true;
            }
        }
        for(int i=1;i<=s1.length();i++){
            if(s1.substring(0,i).equals(s3.substring(0,i))){
                dp[i][0] = true;
            }
        }
        for(int i=1;i<=s1.length();i++){
            for(int j=1;j<=s2.length();j++){
                if(dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1) || dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1)){
                    dp[i][j] = true;
                }
            }
        }
        return dp[s1.length()][s2.length()];
    }
}


//Recursion
public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1.length() + s2.length() != s3.length())
            return false;
        char[] c1 = new char[s3.length()];
        char[] c2 = new char[s3.length()];
        for(int i =0; i
      
      

这个题目是一道很有学习价值的DP题目,应该放在DP章节专门整理,承上启下。
简要的记录几点思路过程。
1.  abbabba  对于这样一个回文,很自然的递推关系 :  bbabb是回文, 并且 abbabba 俩头相等,即a==a 此处。这个递推关系用坐标表示呢?
dp[i][j]   取决于 dp[i+1][j-1]
2. 一个隐含关系:i <= j   , 暗示了矩阵打右上角。
3. 根据地推关系,i 依赖于 i+1  , 即 i 的下一行 ; j 依赖于 j-1, 即j到前一列。 这暗示了扫描要从下往上,从左向右。

4. 一个小trick,注意递推条件 j-i <= 2的使用。原因画出矩阵便清楚了。第一(对角线列) 二斜列是没有历史信息可用的。


public class Solution {
public String longestPalindrome(String s) {
    if(s == null)
        return "";
    boolean[][] dp = new boolean[s.length()][s.length()];
    String res = "";
    for(int i=s.length()-1;i>=0;i--){
        for(int j=i;j
       
       
        
         res.length()){
                    res = s.substring(i,j+1);
                }
            }
        }
    }
    return res;
    }
}

//Another way, in case I forgot DP
public String longestPalindrome(String s) {
    int left; 
    int right;
    String res = "";
    String temp;
    for(int i=0;i
        
        
         
          res.length()){
            res = temp;
        }
        if(right+1 < s.length()){
            temp =  isPalindrome(s,left,right+1);
            if( temp.length() > res.length()){
                res = temp;
            }
        }
    }
    return res;
}

public String isPalindrome(String s, int left, int right){
    while(left >= 0 && right <= s.length()-1 && s.charAt(left) == s.charAt(right)){
        left --;
        right ++;
    }
    return s.substring(left+1,right);
}
        
        
       
       



滑动窗口的题目,用到了hashset来判别重复。空间换时间。
longest 这种string 的题目,不是dp就是双指针滑动窗口??

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null)
            return 0;
        int res = 0;
        HashSet
       
       
        
         set = new HashSet
        
        
         
         ();
        int left = 0;
        int right = 0;
        for(right=0; right
         
         
        
        
       
       



这个题不喜欢,java 的时间卡的太严,相同思路换换实现就过不了。直接看友链记住好了。
作为滑动窗口的题目,似乎又有点太偏。code ganker有一种滑动窗口的解法,第三遍但时候研究下。


Brain Fuck... An exciting sliding window challenge. Notice how "count" is maintained. it expands as right move, and shrinks as left move, but not necessarily. And the condition of 
modifying "count" is very question specific and is implemented by a HashMap here.
public class Solution {
    public String minWindow(String s, String t) {
        if(s == null || t ==null || s.length() ==0 || s.length() < t.length())
            return "";
        HashMap
        
        
         
          map = new HashMap
         
         
          
          ();
        for(char c : t.toCharArray()){
            if(!map.containsKey(c)){
                map.put(c,1);
            }else{
                map.put(c, map.get(c)+1);
            }
        }
        int count = 0;
        int right = 0;
        int left = 0;
        int minStart = 0;
        int min = s.length() + 1;
        for(right=0;right
          
          
           
           = 0){
                    //每个T中的char只出现一次,出现多次也不计入count
                    count ++;
                }
                while(count == t.length()){
                    if(right - left + 1 < min){
                        min = right - left + 1;
                        minStart = left;
                    }
                    if(map.containsKey(s.charAt(left))){
                        map.put(s.charAt(left), map.get(s.charAt(left)) + 1 );
                        if(map.get(s.charAt(left)) > 0){
                        //对应count++的操作。多余的出现视为不在T内,所以都要跳过。
                            count --;
                        }
                    }
                    left++;                    
                }
            }
        }
        
    if(min>s.length()) //这种检查总是有益无害的 
        return "";  
    return s.substring(minStart,minStart+min);
    }
}
          
          
         
         
        
        





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值