IPv6
Description
众所周知,由于IPv4的网络地址资源有限,严重制约了互联网的应用和发展。而IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍。现给出一个128位(二进制下)的IPv6地址,对其按照以下规则化简为最短长度的形式。
- 以十六进制表示这个地址,每4位(十六进制下)划分为一个区域,用 ‘:’ 隔开。比如’0000:0000:1a7b:0456:9876:0000:0000:0000’.
- 每个区域内的前导0可省略。比如上述的地址可进一步化简为 ‘0:0:1a7b:456:9876:0:0:0’.
- 连续为0的多个区域(2个或以上)可化简为 ‘::’,仅可使用一次。比如上述地址可再次化简为 ‘::1a7b:456:9876:0:0:0’ 或 ‘0:0:1a7b:456:9876::’ ,但是不能化简为 ‘::1a7b:456:9876::’ .
- 如果化简后存在多个长度相同的地址,输出字典序最小的地址(将地址看作字符串)。
Input
输入包含多组数据。第一行为一个正整数 T (1≤T≤1000),表示数据组数。
每组输入包含一个128位的二进制数字,表示一个IPv6地址。
Output
对于每组数据,输出该地址进行化简后的最短长度形式。
思路:
将二进制转化为10进制,然后用%x输出16进制。
化简过程(ASCLL 字母 > : > 数字):
找到最长连续0;如果存在多个,则有两种情况。
1.如果存在中间的,更新中间的长度会少一个,例如:0:0:aaaa:0:0:aaaa:0:0
这样中间会多化简一个冒号,存在多个中间相同的,则更新后面那个。
2.如果存在两边相同的,更新最后一个,字典序会更小。例如0:0:aaaa:aaaa:0:aaaa:0:0
我们存一个id,让id去模拟这个找需要化简的第一个0的位置,k为最长连续0的个数。
AC代码
#include<bits/stdc++.h>
using namespace std;
int ipv6[10];
//2进制转换为10进制
int transform10(string t) {
int ans = 0;
for (int i = 0; i < t.size(); ++i) {
ans = ans * 2 + t[i] - '0';
}
return ans;
}
void solve() {
string s;
cin >> s;
int cnt = 0;
for (int i = 0; i < 128; i += 16) {
string t = "";
for (int j = i; j < i + 16; ++j) {
t += s[j];
}
ipv6[++cnt] = transform10(t);
}
int id = 0, k = 1;
for (int i = 1; i <= 8; ++i) {
int x = 0;
for (int j = i; j <= 8; ++j) {
if (ipv6[j] != 0) {
break;
}
x++;
}
if (x > k) {
id = i;
k = x;
} else if (x >= 2 && x == k) {
if (id == 1 && i + x == 9) {
id = i;
} else if (i + x != 9){
id = i;
}
}
}
k--;
for (int i = 1; i <= 8; ++i) {
if (i == id) {
if (id + k != 8) {
if (id == 1) {
printf("::");
} else {
printf(":");
}
} else {
if (id == 1) {
printf("::\n");
} else {
printf(":\n");
}
}
i += k;
} else {
if (i != 8) {
printf("%x:", ipv6[i]);
} else {
printf("%x\n", ipv6[i]);
}
}
}
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; ++i) {
solve();
}
return 0;
}
贴一个大佬代码。
AC代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
char s[MAXN],v6[MAXN];
char ans[MAXN],str[MAXN];
void to() {
int len = strlen(s + 1);
int t = 0,ind = 1,index = 0;
for (int i = 1; i <= len; i++) {
t = t * 2 + (s[i] - '0');
// 每4位2进制转成一位16进制
if (i % 4 == 0) {
if (t < 10) {
s[ind++] = t + '0';
} else {
s[ind++] = t - 10 + 'a';
}
t = 0;
}
// 每4位16进制数处理一次,去除前导0和加冒号
if (i % 16 == 0) {
bool flag = true;
int cnt = 0;
for (int j = ind - 4; j < ind; j++) {
if (s[j] == '0' && flag) {
cnt++;
} else {
v6[index++] = s[j];
flag = false;
}
}
if (cnt == 4) {
v6[index++] = '0';
}
v6[index++] = ':';
cnt = 0;
flag = true;
}
}
s[ind] = '\0';
v6[index - 1] = '\0';
}
int findZero(int ind, int len) {
for (int i = ind; i < len; i++) {
if (v6[i] == '0' || v6[i] == ':') {
continue;
}
return i - 1;
}
return len;
}
void solve() {
scanf("%s",s + 1);
to();
int cnt = 0,ind = 0;
int len = strlen(v6);
int l = 0,r = 0;
strcpy(ans,v6);
for (int i = 0; i < len; i++) {
// v6[i] == '0'且前后都是冒号
if (v6[i] == '0' && (i == 0 || (i - 1 >= 0 && v6[i - 1] == ':' && i + 1 < len && v6[i + 1] == ':'))) {
l = i;
r = findZero(i,len);
i = r;
// 连续为0的区域有2个或以上
if (r - l >= 2) {
// 缩减后的字符串
for (int j = 0; j < len; j++) {
if (j == l) {
while (j < r) {
j++;
}
str[ind++] = ':';
str[ind++] = ':';
} else {
if (j + 1 != l) {
str[ind++] = v6[j];
}
}
}
str[ind] = '\0';
// 更新答案
if (strlen(str) < strlen(ans)) {
strcpy(ans,str);
} else if (strlen(ans) == strlen(str) && strcmp(str,ans) < 0) {
strcpy(ans,str);
}
ind = 0;
}
}
}
printf("%s\n",ans);
}
int main() {
int T;
scanf("%d",&T);
while (T--) {
solve();
}
return 0;
}