P2815 IPv6地址壓縮
題目背景
(友情提示:IPv6基礎知識曾多次出現在NOIP初賽中)Internet Protocol,互聯網協議,即為我們常說的IP。我們目前常說的IP主要指它的第四版,即IPv4,它由IETF於1981年發布。它的地址長度是32個二進制位,因此也就有2^32個IP地址可供使用,約為43億,在當時,誰也沒有料想到IPv4如此龐大的地址長度會有用完的一天。
在21世紀的今天,互聯網的蓬勃發展早就了我們如今便利的生活。當下,世界人口已超過70億,計算機和各種聯網設備已經走入千家萬戶,而不再僅是上個世紀80年代科學家們的工具。此時便出現了人們日益增長的聯網設備需要同落后IPv4地址長度之間的矛盾。盡管可以通過網絡地址翻譯能技術來共享IP地址,臨時解決枯竭的問題,但顯然不是長久之計。
IETF也有先見之明,早早地於1998年發布了IPv6協議,從微軟2006年發布的Windows Vista開始成為默認安裝的網絡協議。作為IPv4的繼任者,它的地址長度為128個二進制位,也就是2^128個IP地址可供使用。然而面對這冗長的地址,一位記憶力不好的網絡工程師小明在配置路由表時遇到了許許多多多的困難,現在他找到了你,希望你幫忙編寫一個程序來按照IPv6地址標准的格式壓縮規則來壓縮IPv6地址。
題目描述
IPv6格式
IPv6二進位制下為128位長度,以16位為一組,每組以冒號“:”隔開,可以分為8組,每組以4位十六進制方式表示。
例如:2001:0db8:0000:0000:0123:4567:89ab:cdef 是一個合法的IPv6地址。
同時IPv6地址在某些條件下可以壓縮:
①每組數字代表的獨立16進制數可以省略前位的0。
例如上面的IPv6地址可被壓縮為:
2001:db8:0:0:123:4567:89ab:cdef
②可以用雙冒號“::”表示一組0或多組連續的0,但只能出現一次
例如上面的IPv6地址可被壓縮為:
2001:db8::123:4567:89ab:cdef
請你幫助記憶力不好的網絡工程師小明解決他遇到的問題。
規則補充:
①輸入數據為完全展開的IPv6地址,確保輸入的IPv6地址不含雙冒號,每組地址省略的0都會被補充上去。
②雙冒號只能使用一次,因此我們壓縮最長的全0組
比如:2001:0db8:0000:0000:1:0000:0000:0000
我們壓縮為2001:db8:0:0:1::,而非2001:db8::1:0:0:0
③雙冒號只能只用一次,因此我們在我們遇到地址中多個連續全0組長度相同時,我們壓縮最前面的一個。
2001:0db8:0000:0000:ffff:0000:0000:1
壓縮為2001:db8::ffff:0:0:1,而非2001:db8:0:0:ffff::1
④輸入的IPv6地址可能無法被壓縮,因此請照原樣輸出。
提示:本題所示的壓縮規則與macOS(Darwin)默認的IPv6地址顯示方式相同,而Windows和Linux只遇到一組全0時不會使用::進行壓縮。但用此方法壓縮過的IPv6地址一樣可以被Windows和Linux正確識別。
例如:2001:0db8:ffff:0000:0123:4567:89ab:cdef
Darwin壓縮為:2001:db8:ffff::123:4567:89ab:cdef
Linux、Windows壓縮為:2001:db8:ffff:0:123:4567:89ab:cdef
輸入格式
一串39個字符的字符串,代表一個完全展開的IPv6地址
輸出格式
一串壓縮后的IPv6地址
輸入輸出樣例
輸入 #1
240e:0014:6000:0000:0000:0000:0000:0001
輸出 #1
240e:14:6000::1
輸入 #2
2001:0db8:0000:0000:0000:0000:0000:0001
輸出 #2
2001:db8::1
輸入 #3
2001:4860:4860:0000:0000:0000:0000:8888
輸出 #3
2001:4860:4860::8888
輸入 #4
2400:8900:e000:0010:0000:0000:0000:0000
輸出 #4
2400:8900:e000:10::
輸入 #5
0000:0000:0000:0000:0000:0000:0000:0000
輸出 #5
::
輸入 #6
0000:0000:0000:0000:0000:0000:0000:0001
輸出 #6
::1
輸入 #7
2001:0db8:ffff:0000:0123:4567:89ab:cdef
輸出 #7
2001:db8:ffff::123:4567:89ab:cdef
輸入 #8
1234:5678:9abc:def0🔢5678:9abc:def0
輸出 #8
1234:5678:9abc:def0🔢5678:9abc:def0
輸入 #9
0001:0000:0000:0000:0000:0000:0000:0001
輸出 #9
1::1
輸入 #10
0000:0000:0000:0000:0000:0000:0001:0002
輸出 #10
::1:2
【思路】
模擬
【題目大意】
將完整的的IPv6的顯示方式壓縮為macOS(Darwin)默認的IPv6地址顯示方式
【題目分析】
壓縮方式即為:
將每一組數 (每一組數是沒有被:隔開的連續的4個數)的前導0去掉
不過如果是0000那就只能壓縮為0
將最長的連續的0000這樣的串可以壓縮為::
比如0000:0000:0000:0000就可以壓縮為::
如果有兩個相同長度的那就替換前面的
【核心思路】
先找出最長的連續0000串第一個數的位置
到時輸出輸出完成::之后直接跳到這個串的最后一位就好了
總的來說上面這個還是比較好處理的
去除前導0才是最難的
這個時候就會有人說了,這不就是一個while循環搞的定的嘛,哪里難了
這個時候很容易會Wa掉第一個點
本人就出在這個問題上面
因為一般用while循環判斷是不是這組數的最后一個一般會用下一個是不是:來判斷
但是!
想沒想過最后一組數的最后一位怎么判斷?這個后面可沒有:
很容易被忽略的哦
但是知道問題所在之后稍微特判一下就可以了
【提供一組小數據】
關於第一個點錯誤的樣例
abcd:0000:0000🔡0012:0000:0001:0000
正解: abcd:🔡12:0:1:0
錯解: abcd:🔡12:0:1:
【完整代碼】
#include<iostream>
#include<cstdio>
#include<cstring>.
#include<string>
using namespace std;
string s;
int main()
{
cin >> s;
int l = s.length();
int a = 0;
int js = 0;//記錄目前0串的長度
int M = 0;//記錄最大的幾組0的長度
int wz = -1;//最長0那一串中第一個0的位置
int last = -1;//上一個0串的位置
for(register int i = 0;i < l;i += 5)
{
if(s[i] == '0' && s[i + 1] == '0' && s[i + 2] == '0' && s[i + 3] == '0')//一組完整的0
{
if(last == i - 5)
{
last = i;
}
else
{
js = 0;
last = i;
a = i;
}
js ++;
}
if(js > M)
{
M = js;
wz = a;
}
}
int jj = 0;
while(jj < l)
{
if(jj == wz)
{
if(jj == 0)
cout << ":";
jj += M * 5 - 1;
if(jj == l)
cout << ":";
}
else
{
if(jj % 5 == 0)
{
if(jj >= 35)//如果是最后一組的話,那就不能根據下一個是不是:來判斷這個0是不是這組數中最后一個數了
{
while(s[jj] == '0' && jj + 1 != l)
{
jj ++;
}
}
else
{
while(s[jj] == '0' && s[jj + 1] != ':')
{
jj ++;
}
}
}
cout << s[jj];
jj ++;
}
}
cout << endl;
return 0;
}
/*
2001:0db8:0000:0000:0123:4567:89ab:cdef
012345678901234567890123456789012345678
abcd:0000:0000:abcd:0012:0000:0001:0000
*/