题解 CF250B 【Restoring IPv6】
0AND1STORY
2019-01-27 15:29:14
这道题和
>[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)