这个代码测试只能拿40分,大家看看就好,等我有时间了再改改…
一、题目
二、分析
整个题目可以分成以下几部分:输入、排序、从小到大合并、同级合并、输出
1.输入
输入可能有三种形式,但要统一成一种形式,为了方便后面的排序,我建了一IP类,包含32位二进制IP地址字符串和它的前缀长度。用ArrayList存储IP对象。
遍历输入的ip字符串,记录遇到小数点的次数和是否遇到斜杠,从而可以判断输入的ip地址的类型。与此同时,在遍历ip字符串同时,记录数字字符并转换为二进制字符串,每当遇到小数点或者斜杠时更新该字符串。(这样做需要单独考虑输入为纯数字的情况)
最后,如果输的ip是标准型,则直接存储二进制字符串;若是省略后缀型,则补全二进制字符串后面缺的0;若是省略长度型,后缀长度即为(小数点个数+1)*8.
2.排序
实现Comparator借口,重写compare函数对List排序。
3.从小到大合并
遍历存储IP对象的链表,比较当前IP和下一个IP(a,b)。若a的后缀长度大于b的后缀长度,b的匹配集一定不是a的匹配集的子集,直接比较下一项。否则,遍历a,b的二进制字符串,若前a.len位都相同,则b的匹配集是a的匹配集的子集,删除b,同时指针前移。
4.同级合并
仍是遍历存储IP对象的链表,比较当前IP和下一个IP(a,b),对a的前缀长度减一,得到aa,若aa是合法的并且a和b的前缀长度相同,开始判断a,b匹配集的并集是否和aa的匹配集相同:遍历a,b的前len-1位,若每一位都相同,则表示可以用aa替代a和b。替代可以用set函数,用aa替代a,同时删除b。然后判断aa是否是链表的第一个元素,若不是则需要前移指针。
5.输出
遍历链表中每一个IP对象。每八位二进制转换为1个十进制数,加上小数点,最后的斜杠后后缀输出即可。
三、代码
package cidr合并;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
class IP {
String ip; // 32位二进制
int len; // 前缀长度
public IP(String ip, int len) {
this.ip = ip;
this.len = len;
}
}
class IPComparator implements Comparator<IP> {
@Override
public int compare(IP o1, IP o2) {
long a =Long.parseLong(o1.ip,2)-Long.parseLong(o2.ip,2);
if(a==0)
return o1.len-o2.len>0?1:-1;
else
return a>0?1:-1;
} //按照IP和前缀长度升序排序
}
public class Main {
static int n;
static List<IP> ipSet;
private static void storeInput(String ip) //存储输入
{
int pnum = 0; //'.'出现的次数
boolean flag = false; //'/'出现为true
int len; //IP前缀
String number = ""; //记录8位2进制
String bin = ""; // 32位二进制IP地址
for (int i = 0; i < ip.length(); i++)
{
char pos = ip.charAt(i);
if (pos >= '0' && pos <= '9')
number += ip.charAt(i);
else if (ip.charAt(i) == '.') {
bin += String.format("%08d", Integer.parseInt(Integer.toBinaryString(Integer.parseInt(number))));
//用format函数对不足8位的二进制字符串前面补0
number = "";
pnum++;
continue;
} else {
bin += String.format("%08d", Integer.parseInt(Integer.toBinaryString(Integer.parseInt(number))));
number = "";
flag=true;
break;
}
}
if(pnum!=3) //省略后缀型
{
if(pnum==0&&flag==false) //若输入的ip是一个纯数字,上面的循环处理不到
bin=String.format("%08d", Integer.parseInt(Integer.toBinaryString(Integer.parseInt(ip))));
int lack = 32-bin.length();
for(int i=0;i<lack;i++) //补齐后面的0
bin+="0";
}
if(!flag) //省略长度型
len=(pnum+1)*8;
else
len = Integer.parseInt(ip.substring(ip.indexOf('/') + 1)); // 获取ip前缀
ipSet.add(new IP(bin,len));
}
private static void ascCombine() //从小到大合并
{
for(int i=0;i<ipSet.size()-1;i++)
{
boolean flag = true;
IP a = ipSet.get(i);
IP b = ipSet.get(i+1);
if(a.len>b.len) //b的匹配集一定不是是a的子集
continue;
else
{
for(int j=0;j<a.len;j++)
{
if(a.ip.charAt(j)!=b.ip.charAt(j))
{
flag=false;
break;
}
}
}
if(flag) //b的匹配集是a的子集,删除b
{
ipSet.remove(i+1);
i--;
}
}
}
private static void sameCombine() //同级合并
{
LOOP1:for(int i=0;i<ipSet.size()-1;i++)
{
IP a = ipSet.get(i);
IP b = ipSet.get(i+1);
IP aa = new IP(a.ip,a.len-1);
if(a.ip.length()==b.ip.length()&&checkIP(aa))
{
for(int j=0;j<aa.len;j++)
{
if(a.ip.charAt(j)!=b.ip.charAt(j))
continue LOOP1;
}
ipSet.set(i, aa); //用aa替换a,并删除b
ipSet.remove(i+1);
if(i!=0) i--;
}
}
}
private static boolean checkIP(IP t) //检查IP前缀是否合法
{
for(int i=t.len;i<t.ip.length();i++)
if(t.ip.charAt(i)!='0')
return false;
return true;
}
private static void print()
{
for(int i=0;i<ipSet.size();i++)
{
String ip = ipSet.get(i).ip;
String d_ip="";
String s ="";
for(int j=0;j<ip.length();j++)
{
s+=ip.charAt(j);
if(j%8==7&&j!=31)
{
d_ip+=Integer.parseInt(s, 2);
d_ip+='.';
s="";
}
}
d_ip+=Integer.parseInt(s,2);
d_ip+='/';
d_ip+=ipSet.get(i).len;
System.out.println(d_ip);
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
ipSet = new ArrayList<IP>();
for (int i = 0; i < n; i++) {
String ip = in.next();
storeInput(ip);
}
Collections.sort(ipSet, new IPComparator());
ascCombine();
sameCombine();
print();
}
}