一段字符串由4-12位数字组成,是由ip地址去掉点组成的,求倒推出所有的可能原ip

0.999的循环等于1
真空不是没有东西,是充满了东西

前言

在一次面试中,面试官让我做这题,我还以为他有什么更好的方法。他否定了我的想法,我不服,于是找他要的的解法,测试结果,他的方法消耗的时间是最多的。47w纳秒左右,然而网上找的回溯法才32w纳秒左右,而我做的只需3000纳秒。可惜我还是没有找到工作。

我做的

一个个试,用判断代替循环,用空间节省时间。应该没bug,平均用时3000纳秒

package math;

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

/**
 * 1.1.2.3034 一开始就判断尾部是否<255,成立再判断是否0开头。 
 * 1.1.23.034 移动了最后这个点,就要判断这个点的两边的数据的合理性(突然发现你那段代码有这个bug,如果一段前面可以为0,那每个字符串都应该是12位)。
 * 1.1.230.34 移动了最后这个点,就要判断这个点的两边的数据的合理性。 
 * 1.12.3.034 最后的点后移了两次,再移动就是四位数了,不合理,这时候开始移动第二个点,第三个点紧跟第二个点的后面。判断第二个点前,第三个点后的数的合理性。
 * 1.12.30.34 然后再动第三个点,再判断移动点前后数据合理性。 
 * 1.12.303.4 然后再动第三个点,再判断移动点前后数据合理性。
 * 1.123.0.34 移动第二个点,同时第三个点紧跟第三点后面,判断第二点前,第三点后的数合理性。
 *  
 * @author zhan几次手动查找之后我发现了一些规律,有部分规律比较复杂,暂时没有用程序表达出来!
 *
 */
public class OneByOneTest {
	public static void main(String[] args) {
		long startTime = System.nanoTime();
		String ip = "0650114";
		int length = ip.length();
		//随便校验一下
		if (length < 4 || length > 12) {
			System.out.println("输入不合法");
		} else {
			long endTime = System.nanoTime();
			System.out.println(search(ip, length).toString() + " 共用时间: " + (endTime - startTime)+"纳秒");
		}
	}

	//另外定义一个方法实现逻辑
	public static List<StringBuilder> search(String ip, int length) {
		//转成StringBuilder效率高一些
		StringBuilder newIp = new StringBuilder(ip);
		//3点分割,定义最后一个点的位置,一定是从第a处空隙开始的
		int a = 3;
		//因为最后一段不可能超过三位数,如果给的字符串比较长,最后一点就从倒数第三个空隙开始
		if (length - 3 > 3) {
			a = length - 3;
			//如果倒数第三、二个数是0,那么第三个点肯定不能在0前面的
			while (a < length - 1 && Integer.valueOf(ip.substring(a, a + 1)) == 0) {
				a++;
			}
		}
		List<StringBuilder> ipList = new ArrayList<>();
		for (int i = 1; i < 4 && i < length - 2; i++) {
			//判断一下,如果第一个数时0,那么第一个点只能移动一次
			String l = newIp.substring(0, 1);
			if("0".equals(l) && i>1) {
				break;
			}
			//判断一下,第一段的数值不能大于255
			int m = Integer.valueOf(newIp.substring(0, i));
			if(m>255) {
				break;
			}
			//第二段是从第一段的后面以为开始尝试的
			for (int j = i + 1; j < i + 4 && j < length - 1; j++) {
				//判断一下,第二段的数值不能大于255,且第二段是2位以上的时候,开头不能是零
				String e = newIp.substring(i, i + 1);
				String h = newIp.substring(i, j);
				int f = Integer.valueOf(h);
				if (f > 255) {
					continue;
				}
				if (!(e.equals(h)) && "0".equals(e)) {
					continue;
				}
				//最后一段的开始位置一定在第二段的后面
				for (int k = j >= a ? j + 1 : a; k < length; k++) {
					//判断一下,最后一段被截出来后,两位数以上时,第一位不能是0,是0就把点后移
					if (k < length - 1 && Integer.valueOf(newIp.substring(k, k + 1)) == 0) {
						k++;
						continue;
					}
					//判断一下倒数第二段的合理性和最后一段的值不大于255
					int b = Integer.valueOf(newIp.substring(k, length));
					String c = newIp.substring(j, j + 1);
					String g = newIp.substring(j, k);
					int d = Integer.valueOf(g);
					if (b <= 255 && d <= 255) {
						if (!(c.equals(g)) && "0".equals(c)) {
							continue;
						}
						//合理则保存在集合中
						ipList.add(new StringBuilder(ip).insert(k, ".").insert(j, ".").insert(i, "."));
					}
				}
			}
		}
		return ipList;
	}
}

面试官做的

用排列组合出所有的可能性,由很多冗余的for循环,然后遍历,并判断合理性。此代码有bug,而且平均要用47w纳秒

package math;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Test4 {

    private static Map<Integer, List<IpPoint>> data = new HashMap<>();

    static {
        for (int a = 1 ; a < 4 ; a++){
            for (int b = 1 ; b < 4 ; b++){
                for (int c = 1 ; c < 4 ; c++){
                    for (int d = 1 ; d < 4 ; d++){
                        int length = a+b+c+d;
                        IpPoint ipPoint = new IpPoint(a,b,c,d);
                        if(data.containsKey(length)){
                            data.get(length).add(ipPoint);
                        }else{
                            List<IpPoint> list = new ArrayList<>();
                            list.add(ipPoint);
                            data.put(length,list);
                        }
                    }
                }
            }
        }


    }

    public static void main(String[] args) {
    	long startTime = System.nanoTime();
        String ip = "0650114";
        List<String> ipList = new ArrayList<>();
        List<IpPoint> list = data.get(ip.length());
        if(list != null && list.size() > 0){
            for (IpPoint ipPoint : list){
                int a = Integer.valueOf(ip.substring(0,ipPoint.getA()));
                if(a < 0 || a > 255){
                    continue;
                }
                int b = Integer.valueOf(ip.substring(ipPoint.getA(),ipPoint.getA()+ipPoint.getB()));
                if(b < 0 || b > 255){
                    continue;
                }
                int c = Integer.valueOf(ip.substring(ipPoint.getA()+ipPoint.getB(),ipPoint.getA()+ipPoint.getB()+ipPoint.getC()));
                if(c < 0 || c > 255){
                    continue;
                }
                int d = Integer.valueOf(ip.substring(ipPoint.getA()+ipPoint.getB()+ipPoint.getC(),ipPoint.getA()+ipPoint.getB()+ipPoint.getC()+ipPoint.getD()));
                if(d < 0 || d > 255){
                    continue;
                }
                ipList.add(a+"."+b+"."+c+"."+d);
            }
        }
        long endTime = System.nanoTime();
        System.out.println(ipList+" Time taken:"+(endTime-startTime)+"纳秒");
    }


    /**
     * ip组成
     */
    public static class IpPoint{

        private int a;
        private int b;
        private int c;
        private int d;

        public IpPoint(int a, int b, int c, int d) {
            this.a = a;
            this.b = b;
            this.c = c;
            this.d = d;
        }

        public int getA() {
            return a;
        }

        public int getB() {
            return b;
        }

        public int getC() {
            return c;
        }

        public int getD() {
            return d;
        }

        @Override
        public String toString() {
            return "IpPoint{" +
                    "a=" + a +
                    ", b=" + b +
                    ", c=" + c +
                    ", d=" + d +
                    '}';
        }
    }
}

网上找的

回溯法,此代码比较严谨,平均用时30w纳米哦

package math;

import java.util.ArrayList;
import java.util.List;
/**
 * 回溯法求数字回ip
 * @author zhan
 *
 */
public class Solution {
	public static void main(String[] args) {
		long startTime = System.nanoTime();
		List<String>  list = restoreIpAddresses("0650114");
		long endTime = System.nanoTime();
        System.out.println(list+"耗时:"+(endTime-startTime));
	}
    public static List<String> restoreIpAddresses(String s) {
        if (null == s || s.length() == 0) {
            return new ArrayList<String>();
        }
 
        List<String> list = new ArrayList<String>();
        method(s, list, new ArrayList<String>(), 0, 4);
        return list;
    }
 
    private static void method(String s, List<String> list, List<String> tmp, int index, int count) {
    	if(index == s.length() && tmp.size() == 4){
    		list.add(join(tmp));
    		return;
    	}
        if (count <= 0 || tmp.size() >= 4) {
            return;
        }
 
        for (int i = index; i < index + 3 && i < s.length(); i++) {
            if (isNumValid(s, index, i + 1)) {
                String sub = s.substring(index, i + 1);
                tmp.add(sub);
                method(s, list, tmp, i + 1, count - 1);
                tmp.remove(tmp.size() - 1);
            }
        }
    }
 
    private static String join(List<String> tmp) {
        StringBuilder sb = new StringBuilder();
        			       sb.append(tmp.get(0)).append(".").append(tmp.get(1)).append(".").append(tmp.get(2)).append(".").append(tmp.get(3));
        return sb.toString();
    }
 
    private static boolean isNumValid(String s, int begin, int end) {
        if (begin > end || end - begin > 3) {
            return false;
        }
        if(s.startsWith("0", begin) && end - begin > 1){
            return false;
        }
        
        Integer value = Integer.valueOf(s.substring(begin, end));
        return value >= 0 && value <= 255;
    }
    
}

后话

解决算术问题最好的方式是找规律,或者用公式求解。学编程的时候求1到100的求和你们是不是用for循环,偏偏这种方法是最慢的,明明就有公式,为什么不用?然而,有些问题不是用我们学的数学去限定我们的思路,一定要知道计算机是怎么运行的,从而明白它会做多少的无用功。从而估算算法的性能。我是光信息科学技术专业的,我自豪。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值