开始搜的题解是90分的,因为对java不熟悉,就从里面学了很多东西,indexOf,split,还有处理合并的方法。
最后一个点过不了,我开始我的优化之路。
题解的排序是插入排序,这样是O(n*n*log(n)),我用sort,就可以降到O(n*log(n))。
在合并的过程中,题解的方法模拟的时候用remove操作,这样加上本来需要O(n)的扫描时间,就是O(n*n)复杂度。
把它优化成O(n)。对于这两个合并,我都采用了扫描过程中把需要的点留下来的思路,这样不用花多余的时间去把元素从list里面删除,O(n)扫一遍就好。第二个合并因为还要和之前的比较,我就用了stack,每次用栈顶的元素和待比较的位置进行比较,如果要合并就出栈,把新的结果放在compare里面,空栈时把结果放进来,compare多加一个1。这样可以达到复杂度和比较次数相同的目的。
这样最高的复杂度是排序的时候,需要O(nlog(n)),应该能过,但还是90分。我就被卡住了很久很久。
但是后来俱乐部xzq学长告诉我java的Scanner是很慢的,像c++的cin一样,大数据量需要优化输入输出的。去找了一下优化输入输出的方法,最后过了。无比振奋啊,这就是全网首发的csp15-3 java实现满分题解(蒟蒻的自嗨)!
最开始了解写法和思路的博客,我找的题解https://blog.csdn.net/qq_36143109/article/details/88411854#commentBox
学习加速输入的博客https://www.jianshu.com/p/1184545102c7
import java.io.*;
import java.util.*;
import java.lang.*;
class prefix implements Comparable<prefix>{
public int[] ip;
public int len;
public prefix(int[] ip,int len) {
this.ip = ip;
this.len = len;
}
public int compareTo(prefix x) {
for(int i = 0; i < 4; i++) {
if(this.ip[i] != x.ip[i])
return this.ip[i] - x.ip[i];
}
return this.len - x.len;
}
}
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
/**
* call this method to initialize reader for InputStream
*/
static void init(InputStream input) {
reader = new BufferedReader(
new InputStreamReader(input));
tokenizer = new StringTokenizer("");
}
/**
* get next word
*/
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
//TODO add check for eof if necessary
tokenizer = new StringTokenizer(
reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
}
public class Main {
public static void main(String[] args) throws IOException {
// Scanner in = new Scanner(System.in);
// int n = Integer.parseInt(in.nextLine());
Reader.init(System.in);
int n = Reader.nextInt();
List<prefix> list = new ArrayList<prefix>();
//读入并转换成prefix类
for(int d = 0;d<n;d++) {
// String ip = in.nextLine();
String ip = Reader.next();
//构建prefix对象
int[] Ip = new int[4];
int len = 0;
int index = ip.indexOf("/");
if(index != -1) {
String[] b = ip.split("/");
len = Integer.parseInt(b[1]);
String str = b[0];
if(str.indexOf(".") != -1) b = str.split("\\.");
else b = new String[] {str};
//同时处理了省略后缀型和标准型
for(int i = 0; i < b.length; i++) Ip[i] = Integer.parseInt(b[i]);
for(int i = b.length; i<4 ;i++) Ip[i] = 0;
}else {//省略长度型
String[] b = new String[]{ip};
if(ip.indexOf(".") != -1) b = ip.split("\\.");
len = b.length * 8;
for(int i = 0;i < b.length;i++) Ip[i] = Integer.parseInt(b[i]);
for(int i = b.length; i < 4; i++) Ip[i] = 0;
}
prefix prefix = new prefix(Ip,len);
list.add(prefix);
}
Collections.sort(list);
List<prefix> list1 = new ArrayList<prefix>();
//从小到大合并 从list找到合并后的元素放到list1里面
int size = list.size() - 1;
int min;
int[] ip1, ip2;
//k和k + 1匹配,能匹配则删掉k + 1,继续和新的k + 1匹配
int k = 0, nxt;//nxt指向第一个不能和k合并的值
while(k <= size) {
list1.add(list.get(k));
nxt = k + 1;
if(nxt > size) break;
while(nxt <= size) {
min = Math.min(list.get(k).len, list.get(nxt).len);
ip1 = and(list.get(k).ip,min);
ip2 = and(list.get(nxt).ip,min);
//取更小的长度min min之后的部分截掉变成0, 如果计算结果相同说明它们的匹配集相同
if(cmp(ip1, ip2) == 0)
nxt++;
else break;
}
k = nxt;
}
list.clear();
size = list1.size() - 1;
for(int i = 0; i <= size; i++){
list.add(list1.get(i));
}
//同级合并,永远从栈顶开始找
Stack<prefix> list2 = new Stack<prefix>();
size = list.size();
int compare = 1;
list2.push(list.get(0));
while(compare < size) {
prefix from = list2.pop();
prefix get = can_combine2(from, list.get(compare));
if(get == from) {
list2.push(from);
list2.push(list.get(compare));
compare++;
}
else {
list.set(compare, get);
if(list2.empty()) {
list2.push(list.get(compare));
compare++;
}
}
}
size = list2.size();
for(int i = 0; i < size; i++) list1.set(i, list2.pop());
list.clear();
for(int i = size - 1; i >= 0; i--) list.add(list1.get(i));
// in.close();
StringBuilder sb = new StringBuilder();
for(prefix show:list) {
sb.append(show.ip[0]+"."+show.ip[1]+
"."+show.ip[2]+"."+show.ip[3]+"/"+show.len + "\n");
}
sb.append(" ");
System.out.print(sb);
// for(prefix show:list) {
// System.out.println(show.ip[0]+"."+show.ip[1]+
// "."+show.ip[2]+"."+show.ip[3]+"/"+show.len);
// }
}
public static prefix can_combine2(prefix a, prefix b) {
//如果不能合并,返回a
if(a.len != b.len) return a;
int len = a.len - 1;
int[] ip1 = and(a.ip, len);
int[] ip2 = and(b.ip, len);
if(cmp(ip1, ip2) == 0) return new prefix(ip1, len);
else return a;
}
public static int cmp(int[] x, int[] y) {
for(int i = 0; i < 4; i++) {
if(x[i] > y[i])
return 1;
if(x[i] < y[i])
return -1;
}
return 0;
}
public static int cmp1(prefix a, prefix b) {
//判断a是否比b大, a > b返回1, a < b返回-1,a == b返回0
int[] x = a.ip;
int[] y = b.ip;
for(int i = 0;i < 4;i++) {
if(x[i] > y[i])
return 1;
else if(x[i] < y[i])
return -1;
}
if(a.len < b.len) return -1;
else if(a.len == b.len) return 0;
else return 1;
}
//获得一个前len位和p一样,后面为0的ip地址,用长度为4的数组表示
public static int[] and(int[] p, int len) {
int n = len / 8;
int m = len % 8;
int[] P = new int[4];
for(int i = 0; i < n; i++) P[i] = p[i];
//-1的二进制位全部是1,左移8-m位刚好使末尾8-m位是0
if(n<4) P[n] = p[n] & (-1 << (8 - m));
for(int i = n + 1; i < 4; i++) {
P[i] = 0;
}
return P;
}
}