BOJ 3107 IPv6 (Java)

사람·2025년 3월 13일
0

BOJ

목록 보기
39/76

문제

https://www.acmicpc.net/problem/3107

IPv6은 길이가 128비트인 차세대 인터넷 프로토콜이다.

IPv6의 주소는 32자리의 16진수를 4자리씩 끊어 나타낸다. 이때, 각 그룹은 콜론 (:)으로 구분해서 나타낸다.

예를 들면, 다음과 같다.
2001:0db8:85a3:0000:0000:8a2e:0370:7334

32자리의 16진수는 사람이 읽고 쓰기에 불편하고, 대부분의 자리가 0이기 때문에 아래와 같이 축약할 수 있다.

  1. 각 그룹의 앞자리의 0의 전체 또는 일부를 생략 할 수 있다. 위의 IPv6을 축약하면, 다음과 같다.
    2001:db8:85a3:0:00:8a2e:370:7334
  2. 만약 0으로만 이루어져 있는 그룹이 있을 경우 그 중 한 개 이상 연속된 그룹을 하나 골라 콜론 2개(::)로 바꿀 수 있다.
    2001:db8:85a3::8a2e:370:7334
    2번째 규칙은 모호함을 방지하기 위해서 오직 한 번만 사용할 수 있다.

올바른 축약형 IPv6주소가 주어졌을 때, 이를 원래 IPv6 (32자리의 16진수)로 복원하는 프로그램을 작성하시오.

입력
첫째 줄에 올바른 IPv6 주소가 주어진다. 이 주소는 최대 39글자이다. 또한, 주소는 숫자 0-9, 알파벳 소문자 a-f, 콜론 :으로만 이루어져 있다.

출력
첫째 줄에, 입력으로 주어진 IPv6의 축약되지 않은 형태를 출력한다.

예제 입력 1
25:09:1985:aa:091:4846:374:bb
예제 출력 1
0025:0009:1985:00aa:0091:4846:0374:00bb

예제 입력 2
::1
예제 출력 2
0000:0000:0000:0000:0000:0000:0000:0001

접근

축약하는 규칙 2가지 중 첫 번째(앞자리 0 생략)에 따른 복원은 그냥 생략된 개수만큼 0을 추가해주면 되니 어렵지 않다. 문제는 두 번째 규칙인 ::을 어떻게 처리하느냐인 것 같다.

::을 제외한 나머지 부분은 콜론 1개(:)로만 구분되어 있으므로 전체 IPv6 주소를 일단 ::를 기준으로 한 번 쪼갠 다음 쪼개진 각 부분을 다시 :로 분할해야겠다고 생각했다.


예를 들어 입력이 25:09:1985:aa::374:bb라고 주어졌다면,
우선 ::를 기준으로 나누면 25:09:1985:aa374:bb로 분할된다.

25:09:1985:aa은 4개의 그룹으로, 374:bb은 2개의 그룹으로 이루어져 있다. 그런데 그룹은 총 8개여야 하므로 25:09:1985:aa374:bb 사이에 0만으로 이루어진 그룹이 2개 있음을 알 수 있다.
따라서, 크기가 8인 String 배열 result의 처음 부분(result[0], result[1], result[3])에 25:09:1985:aa를 차례대로 넣고, 끝 부분에 374:bb(result[6], result[7])를 넣은 후 그 사이에 존재하는 빈 공간(result[4], result[5])에는 "0000"을 채워넣으면 된다. 그리고 각 그룹을 순회하며 문자열의 길이가 4 미만인 경우에는 앞에 생략된 0을 필요한 개수만큼 채워주면 복원이 완료된다.

구현

구현할 때 가장 신경 쓰였던 부분은 구분자가 문자열의 처음이나 끝에 존재할 때 java의 String.split() 메소드가 어떻게 동작하느냐였다.
예를 들어 문제에 주어진 두 번째 예제 입력 ::1에 대해 .split("::")를 호출할 때, ["", "1"]과 같이 빈 문자열을 포함한 길이가 2인 String 배열을 반환할지 아니면 그냥 ["1"]을 반환할지에 대해 확신이 없었다.

결론만 말하면,
1. 구분자가 문자열의 맨 앞에 존재할 때는 반환된 배열의 첫 번째 요소가 빈 문자열이 된다.
2. 구분자가 문자열에 마지막에 존재할 때는 반환된 배열의 마지막 요소가 빈 문자열이 된다.

그래서 split한 결과가 빈 문자열인 경우는 따로 처리가 필요했다.
.split("::")를 호출했을 때 반환받은 배열의 요소를 result 배열에 추가할 때, 빈 문자열의 경우는 추가하지 않고 스킵해주었다.

import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String original = br.readLine();
        String[] temp = original.split("::");
        String[] result = new String[8];
        if (temp.length == 1) {
            String[] left = temp[0].split(":");
            for (int i = 0; i < left.length; i++) {
                result[i] = left[i];
            }
        } else {
            String[] left = temp[0].split(":");
            String[] right = temp[1].split(":");
            for (int i = 0, j = 0; i < left.length; i++) {
                if (left[i].isEmpty()) {
                    continue;
                }
                result[j++] = left[i];
            }
            for (int i = right.length - 1, j = 7; i >= 0; i--) {
                if (right[i].isEmpty()) {
                    continue;
                }
                result[j--] = right[i];
            }
        }

        for (int i = 0; i < 8; i++) {
            if (result[i] == null) {
                result[i] = "0000";
                continue;
            }
            if (result[i].length() != 4) {
                StringBuilder sb = new StringBuilder(result[i]);
                while (sb.length() < 4) {
                    sb.insert(0, '0');
                }
                result[i] = sb.toString();
            }
        }
        System.out.println(String.join(":", result));
    }
}

처음에는 original.split("::");의 반환 값인 temp의 크기가 무조건 2 이상일 거라고 생각해서 temp의 크기가 1일 때를 따로 고려하지 않았었는데, 입력이 ::로 주어진 경우 "::"를 구분자로 split했을 때 빈 문자열 하나만 요소로 갖는 배열이 리턴되기에 temp의 크기가 1인 경우도 존재했다. 그래서 이 경우를 따로 처리해주는 코드를 추가했다.

profile
알고리즘 블로그 아닙니다.

0개의 댓글