题解 CF250B 【Restoring IPv6】

0AND1STORY

2019-01-27 15:29:14

Solution

这道题和 >[P2815 IPv6地址压缩](https://www.luogu.org/problemnew/show/P2815) 这道题一样,都是简单的字符串处理 只不过P2815是将完整的ipv6地址压缩,而这道题是将压缩后的ipv6地址展开,原理都差不多 P2815可以用 ```cpp scanf("%x:%x:%x:%x:%x:%x:%x:%x",&s[1],&s[2],&s[3],&s[4],&s[5],&s[6],&s[7],&s[8]); ``` 的方法读入,而这一道题因为要处理双冒号,而且读入数的个数也无法确定,所以这道题只能用读入整个字符串的方法来做 根据题目很容易就想到做法, ### 步骤如下: 0. 宏定义。。。 ```cpp //不用在意这些宏定义,方便而已 #define main int main() #define Void inline void #define Int register int #define for(i, l, r) for(Int i = l; i < r; i ++) #define endmain return 0 ``` 1. 读入地址存入字符串s ```cpp cin >> s; ``` 2. 将字符串s中的数据转存到a数组中,用0xfffff代替双冒号 ```cpp Int x = 0; for (i, 0, s.size() - 1) { //如果发现双冒号,则将双冒号解释为0xfffff if (s[i] == ':' && s[i + 1] == ':') { a.push_back(x); //因为发现双冒号相当于发现了冒号,将读入的数加入a中 x = 0; a.push_back(0xfffff); //将双冒号的标志加入a i ++; //跳过下一个冒号 continue; } //如果不是16进制数,即为冒号,则将已读入的数加入a if (!isdigit(s[i]) && (s[i] < 'a' || s[i] > 'f')) { a.push_back(x); x = 0; continue; } else //如果是16进制的数,则用x记录读入到的数 { //因为是16进制数,所以要乘16,x << 4就相当于乘16 x = (x << 4) + (isdigit(s[i]) ? s[i] - '0' : s[i] - 'a' + 10); } } a.push_back(x); //以为地址最后不会有冒号,所以将最后一个数加入a //如果地址最后为`:`,即双冒号在末尾, //上一步多加了一个数,这一步将这个数删去 if (s[s.size() - 1] == ':') a.pop_back(); //否则最后是一个数字,因为扫描数字时没有扫描到最后一个, //所以这一步将最后一位加上 else a[a.size() - 1] = (a[a.size() - 1] << 4) + (isdigit(s[s.size() - 1]) ? s[s.size() - 1] - '0' : s[s.size() - 1] - 'a' + 10); ``` 3. 将a数组按照完整格式输出,计算"::"所代表的0的个数,并输出即可 ```cpp for (i, 0, a.size() - 1) //如果不是双冒号,即为16进制数,进行输出 if (a[i] != 0xfffff) //%04x即为以列宽为4输出,空格用0填充 printf("%04x:", a[i]); else { //如果为双冒号,计算双冒号代替的0的个数并输出 //(此时双冒号不在最后) for (j, 0, 8 - a.size() + 1) printf("0000:"); } //如果双冒号不在最后,输出最后一个数 if (a[a.size() - 1] != 0xfffff) printf("%04x", a[a.size() - 1]); else //否则输出双冒号所代替的0 { //这里三目运算符用来判断是否需要在末尾加上冒号 for (i, 0, 8 - a.size() + 1) printf("0000%s", i == 8 - a.size() ? "" : ":"); } printf("\n"); //注意最后必须要换行,因为是多组数据 ``` ### AC代码如下: ```cpp #include <cstdio> #include <cctype> #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; //不用在意这些宏定义,方便而已 #define main int main() #define Void inline void #define Int register int #define for(i, l, r) for(Int i = l; i < r; i ++) #define endmain return 0 //定义一个a数组用来存从原地址中的数 vector<int> a; //用来存读入的原地址 string s; //从原地址中读入数存入a数组,这一段类似于快读 Void input() { Int x = 0; for (i, 0, s.size() - 1) { //如果发现双冒号,则将双冒号解释为0xfffff if (s[i] == ':' && s[i + 1] == ':') { a.push_back(x); //因为发现双冒号相当于发现了冒号,将读入的数加入a中 x = 0; a.push_back(0xfffff); //将双冒号的标志加入a i ++; //跳过下一个冒号 continue; } //如果不是16进制数,即为冒号,则将已读入的数加入a if (!isdigit(s[i]) && (s[i] < 'a' || s[i] > 'f')) { a.push_back(x); x = 0; continue; } else //如果是16进制的数,则用x记录读入到的数 { //因为是16进制数,所以要乘16,x << 4就相当于乘16 x = (x << 4) + (isdigit(s[i]) ? s[i] - '0' : s[i] - 'a' + 10); } } a.push_back(x); //以为地址最后不会有冒号,所以将最后一个数加入a //如果地址最后为`:`,即双冒号在末尾, //上一步多加了一个数,这一步将这个数删去 if (s[s.size() - 1] == ':') a.pop_back(); //否则最后是一个数字,因为扫描数字时没有扫描到最后一个, //所以这一步将最后一位加上 else a[a.size() - 1] = (a[a.size() - 1] << 4) + (isdigit(s[s.size() - 1]) ? s[s.size() - 1] - '0' : s[s.size() - 1] - 'a' + 10); } Void work() { //记得将a数组清空 a.clear(); s = ""; cin >> s; input(); //将a数组中的数据读入a数组 for (i, 0, a.size() - 1) //如果不是双冒号,即为16进制数,进行输出 if (a[i] != 0xfffff) //%04x即为以列宽为4输出,空格用0填充 printf("%04x:", a[i]); else { //如果为双冒号,计算双冒号代替的0的个数并输出 //(此时双冒号不在最后) for (j, 0, 8 - a.size() + 1) printf("0000:"); } //如果双冒号不在最后,输出最后一个数 if (a[a.size() - 1] != 0xfffff) printf("%04x", a[a.size() - 1]); else //否则输出双冒号所代替的0 { //这里三目运算符用来判断是否需要在末尾加上冒号 for (i, 0, 8 - a.size() + 1) printf("0000%s", i == 8 - a.size() ? "" : ":"); } printf("\n"); //注意最后必须要换行,因为是多组数据 } main { Int t; scanf("%d", &t); while (t --) work(); endmain; } ``` #### 最后附上一张AC截图: ![AC截图](https://s2.ax1x.com/2019/01/27/kubUQP.png)