6. 复原IP地址

1. 题目

给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

2. 思路

使用分支剪枝的的方式:

1、一开始,字符串的长度小于 4 或者大于 12 ,一定不能拼凑出合法的 ip 地址(这一点可以一般化到中间结点的判断中,以产生剪枝行为);

2、每一个结点可以选择截取的方法只有 3 种:截 1 位、截 2 位、截 3 位,因此每一个结点可以生长出的分支最多只有 3 条分支;

根据截取出来的字符串判断是否是合理的 ip 段,这里写法比较多,可以先截取,再转换成 int ,再判断。我采用的做法是先转成 int,是合法的 ip 段数值以后,再截取。

3、由于 ip 段最多就 4 个段,因此这棵三叉树最多 4 层,这个条件作为递归终止条件之一;

回溯算法事实上就是在一个树形问题上做深度优先遍历,因此 首先需要把问题转换为树形问题。有些枝叶是没有必要的,把没有必要的枝叶剪去的操作就是剪枝

 

3.实现

剪枝少判断,而且也是先判断截取的 ip 段是否合法,然后用截取函数截取字符串,执行结果上会快一些

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Scanner;

public class Main {
	public static void main(String args[]) {
		Scanner sc = new Scanner(System.in);
		//1. 输入数字字符串。数字字符串的长度要 >=4, 并且<=12
		String str = sc.next();
		while(str.length()<4 || str.length()>12) {
			str = sc.next();
		}
		
		//2.定义队列数组,存放当前合法的ip字符串。例如 "255" "255" "111" "23"
		Deque<String> path = new ArrayDeque<String>(4);
		
		//3.结果集。存放获取的所有合法的ip地址
		List<String> res = new ArrayList<String>();
		
		//4.进行深度优先遍历,获取合法的ip地址。
		// str:输入的数字字符串;  从0位置开始截取,当前要获取的ip地址还有4个数未获取;path:存放当前获取的ip字符串数组
		dfs(str,str.length(),0,4,path,res);
		
		System.out.println(res);
	}
	
	/*(1) 获取合法的ip地址(可有多个)
	 * begin:截取ip端的起始位置
	 * remain:记录当前的ip地址还有多少段未被分割;
	 * */ 
	private static void dfs(String str, int len, int begin, int remain, Deque<String> path, List<String> res) {
		// 1.如果已经遍历完数字字符串,并且获取了ip地址的4个段,则获取了一个合法的ip地址(递归的出口)
		if(begin == len) {
			if(remain == 0) {
				// 将path数组中的4个段用.连接
				res.add(String.join(".", path));
			}
			return;
		}
		// 2.依次遍历1、2、3位的数字
		for(int i=begin;i<begin+3;i++) {
			// 2.1 越界
			if(i>=len) {
				break;
			}
			// 2.2 保证未遍历的数字位数不能超过remain*3
			if(remain*3<len-i) {
				continue;
			}
			// 2.3 判断当前段是否合法.str中,下标begin到i之间的数字
			if(judge(str,begin,i)) {
				//将合法的数字段存入path最后
				String curNum = str.substring(begin,i+1);
				path.add(curNum);
				
				//递归获取剩余的remain个字段
				dfs(str,len,i+1,remain-1,path,res);
				
				//回溯,将不合法的情况移除
				path.removeLast();
			}
		}
	}

	/*(2)判断str中,下标left到right之间的数字是否合法
	 * */
	private static boolean judge(String str, int left, int right) {
		// 1.数字位数
		int len = right-left+1;
		// 2.如果数字大于1位时,第一个数不能是0
		if(len>1 && str.charAt(left)=='0') {
			return false;
		}
		// 3.将区间内的字符串转化为数字
		int num=0;
		for(int i=left;i<=right;i++) {
			num=num*10+str.charAt(i)-'0';
		}
		// 4.判断数字是否在区间[0,255]
		return num>=0 && num<=255;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值