Leetcode - Restore IP Addresses

Leetcode - Restore IP Addresses_第1张图片

My code:

import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List restoreIpAddresses(String s) {
        if (s == null)
            return null;
        ArrayList result = new ArrayList();
        if (s.length() < 4 || s.length() > 12)
            return result;
        restoreIpAddresses(s, "", 0, 0, 0, result);
        return result;
    }
    
    
    private void restoreIpAddresses(String s, String ip, int begin, int width, int level,
                                    ArrayList result) {
        if (begin + width - 1 >= s.length())
            return;
        else if (level == 4) {
            if (s.length() - begin - width > 0)
                return;
            int ipPart = Integer.parseInt(s.substring(begin, begin + width));
            if (width == 3 && ipPart < 100)
                return;
            else if (width == 2 && ipPart < 10)
                return;
            else if (ipPart > 255)
                return;
            else {
                result.add(ip + Integer.toString(ipPart));
                return;
            }
        }
        else if (level == 0) {
            for (int i = 1; i < 4; i++)
                restoreIpAddresses(s, ip, begin + width, i, level + 1, result);
        }
        else {
            int residueIp = s.length() - begin - width;
            if (residueIp < 4 - level || residueIp > 3 * (4 - level))
                return;
            int ipPart = Integer.parseInt(s.substring(begin, begin + width));
            if (width == 3 && ipPart < 100)
                return;
            else if (width == 2 && ipPart < 10)
                return;
            else if (ipPart > 255)
                return;

            ip += Integer.toString(ipPart) + ".";
            for (int i = 1; i < 4; i++)
                restoreIpAddresses(s, ip, begin + width, i, level + 1, result);
        }
    }
    
    public static void main(String[] args) {
        Solution test = new Solution();
        System.out.println(test.restoreIpAddresses("101023"));
        
    }
}

My test result:

Leetcode - Restore IP Addresses_第2张图片
Paste_Image.png

这道题目和之前 Array里面的 permutation等等很像。不同的是,他只需要插入符合要求的字符串,不需要插入ArrayList, 所以不需要删除一些已经用过的东西。同时,他是分级的,当到达第四级时,就必须停止了。然后还有些corner case。
比如说,我规定这一级的 width = 3, 然后扫描出了, 017. 如果我调用Integer.parseInt 扫出来的就是17,这是对的。但是 017是错误的。应该排除。
还有width =2 , 08 这些也是错误的。
还有可以及时停止扫描来节省时间。比如当扫描到第二层时,如果后面的字符串长度只有1,那么不用操作了,这肯定是错的,后面还有两级呢。
如果后面的字符串长度 >6了,那也不需要扫描了,因为后面最多也就6位数字。
然后排除掉这些corner case后,程序就会跑的这么快了。
**
下面说下,我刚才才注意到的一个知识点。其实一开始我的代码不是这样的, 或者说有个细节不是这样的。也就是 ip + s.substring(begin, begin + width) 还是 ip + Integer.toString(ipPart); 其实两个方法达到的效果是相通的,但算法复杂度完全不同。下面粘上用 toString() 测试的图片。**

Leetcode - Restore IP Addresses_第3张图片
Paste_Image.png

具体string这一块其实有很多需要分析的。比如这里。
将一个 integer转换成string,怎么样更快?
Integer.toString(a); or String.valueOf(a); or String s = "" + a;
现在可以确定的是,最后一个很消耗资源。

Leetcode - Restore IP Addresses_第4张图片
Paste_Image.png

http://it.deepinmind.com/java/2014/03/24/java-string-performance.html

有时间再好好研究下 string 吧。里面讲究太多了。

**
总结: String, backtracing, Permutation thinking
**

Anyway, Good luck, Richardo!

My code:

import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List restoreIpAddresses(String s) {
        ArrayList ret = new ArrayList();
        if (s == null || s.length() < 4)
            return ret;
        helper(0, 1, s, new StringBuilder(), ret);
        return ret;
    }
    
    private void helper(int begin, int k, String s, StringBuilder ip, ArrayList ret) {
        if (k == 4) {
            if (s.length() - begin > 3)
                return;
            if (s.charAt(begin) == '0' && s.length() - begin >= 2)
                return;
            int tmp = Integer.parseInt(s.substring(begin, s.length()));
            if (tmp > 255)
                return;
            int len = ip.length();
            ip.append(tmp);
            ret.add(ip.toString());
            ip.delete(len, ip.length());
        }
        else {
            for (int i = 1; i <= 3; i++) {
                if (s.length() - (begin + i) < (4 - k))
                    break;
                if (s.charAt(begin) == '0' && i >= 2)
                    break;
                int tmp = Integer.parseInt(s.substring(begin, begin + i));
                if (tmp > 255)
                    continue;
                int len = ip.length();
                ip.append(String.valueOf(tmp) + ".");
                helper(begin + i, k + 1, s, ip, ret);
                ip.delete(len, ip.length());
            }
        }
    }
    
    public static void main(String[] args) {
        Solution test = new Solution();
        System.out.println(test.restoreIpAddresses("010010"));
    }
}

这道题目还是挺烦的。
有几个corner case需要考虑。
首先,分成k < 4 和 k = 4两种情况。
k < 4 时,需要考虑剩余长度不够再做ip address,此时需要直接退出循环,break
k = 4 时,需要考虑剩余长度过长,不止3位了。此时也是退出dfs

需要考虑扫描出来的数字是否大于255.

需要考虑, 017 是不符合规定的,需要去除。
即任何以0开头的数字,除了0自己,都是违规的。

然后就差不多了。
Anyway, Good luck, Richardo!

My code:

import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List restoreIpAddresses(String s) {
        List ret = new ArrayList();
        if (s == null || s.length() == 0 || s.length() > 12) {
            return ret;
        }
        
        helper(0, s, new StringBuilder(), 0, ret);
        return ret;
    }
    
    private void helper(int begin, String s, StringBuilder ip, int level, List ret) {
        if (begin >= s.length()) {
            if (level == 4) {
                ret.add(ip.deleteCharAt(ip.length() - 1).toString());
            }
            return;
        }
        int len = ip.length();
        for (int i = begin; i < begin + 3 && i < s.length(); i++) {
            String part = s.substring(begin, i + 1);
            int temp = Integer.parseInt(part);
            if (temp >= 256) {
                continue;
            }
            else if (part.charAt(0) == '0' && i > begin) {
                continue;
            }
            ip.append(part + ".");
            helper(i + 1, s, ip, level + 1, ret);
            ip.setLength(len);
        }
    }
}

自己写了出来,感觉比以前的代码简洁多了。
为什么呢?
仔细观察,发现我思考 backtracking 经常有个问题。
什么时候截止 backtracking
比如这里, level = 3 (0-3) 时,就可以停止了,
但我需要在 level = 3时加判断吗?
不该,否则代码会很复杂,和下部分代码也有很多重复。
我应该再下去一层,这才是底层。
所以backtracking 每一层的物理意义是,
这一层,我检验过了,ok, 没问题,下一层吧。
然后一层层下去。
如果这一层有问题,那么就返回。
如果走到最后一层,发现没有问题了,那么就将最终结果插入到容器中。

就是这么个过程。
word pattern 2 也是如此。

还有就是,backtracking 用 stringbuilder 时,可以用 setLength
之类的办法快速清除下一层给这层带来的变化。

还有个细节需要注意。当截下来的数字, > 255 或者,长度大于1却以0开头的话,都是无效的,直接 Ignore

Anyway, Good luck, Richardo! -- 09/19/2016

你可能感兴趣的:(Leetcode - Restore IP Addresses)