[SWEA 1242 : JAVA] 암호코드 스캔
문제
SWEA의 문제를 무단으로 복제하는 것은 금지입니다. 따라서, 링크를 달아두겠습니다.
----> SWEA_1242_암호코드 스캔
풀이
문제의 몇 가지 포인트는 다음과 같습니다.
- 암호코드는 8개의 숫자다.
- 입력이 16진수로 들어온다. (16진수 --> 2진수 변환필요)
- 각 수(0~9)들은 0 1 0 1 순의 비율로 결정된다. ex) 5는 1:2:3:1 비율의 0 1 0 1 값이다. 0110001
- 위 3번의 비율이 1배수가 아닐 수도 있다. ex) 위 5의 비율을 2배수로 표현하면 00111100000011 이다.
- 암호코드가 여러 개 주어진다. (한 행에 여러 개의 암호코드가 있을 수 있다.)
해당 문제의 구현에서 어려운 부분은 4, 5번이라고 생각하기에 해당 부분만 설명하겠습니다.
먼저, 각 수(0~9)들은 0 1 0 1 순으로 나오기 때문에 행을 탐색할 때 뒤에서부터 탐색해야합니다. 앞에서부터 탐색하게되면 맨 앞에 나오는 0이 공백을 의미하는건지 암호코드의 일부를 의미하는지 알 수 없기 때문입니다. 뒤에서부터 탐색하다가 가장 먼저 1을 만나게되면 이는 암호코드의 마지막 부분이라는걸 확신할 수 있기 때문에 해당 부분부터 판별해나갑니다. 여기서 첫 번째 난관이 시작됩니다.
1을 시작지점으로 잡는건 알겠는데, 마지막지점은 어디까지일까요?
암호코드가 23456789(8개 숫자)에 대해 다음과 같이 입력이 들어왔다고 생각해보겠습니다.
괄호 안의 대응되는 숫자와 띄어쓰기는 보기 쉬우라고 적어논 것입니다. 실제 입력에선 이 둘이 없다고 생각해주세요.
000000 0010011(2) 0111101(3) 0100011(4) 0110001(5) 0101111(6) 0111011(7) 0110111(8) 0001011(9) 00000
--> 0000000010011011110101000110110001010111101110110110111000101100000
암호코드 8개의 숫자 중 뒤에서 7개(3456789)는 0과 1이 바뀌는 지점을 찾아 갯수를 구해줄 수 있습니다.
예를들어, 처음 1을 만난 지점에서 1이 2개, 0이 1개, 1이 1개, 0이 3개 순으로 나왔으니 3:1:1:2 비율을 가지는 것을 알 수 있고, 이 비율로 어떤 수를 의미하는지 찾아낼 수 있습니다. 그렇게 계속해 나가다가 마지막 수를 만나면 어떻게 될까요? (암호코드의 가장 첫 숫자 2)
1이 2개, 0이 2개, 1이 1개, ... 0은 몇 개지..? 탐색 기준으로 마지막 수는 첫 인덱스까지 0이란 값을 가집니다.
어떻게 해결할 수 있을까요?
문제를 잘 보면 0 1 0 1 의 4개 비율을 모두 보지 않고, 마지막 3개의 비율만 봐도 알 수 있다는 걸 알 수 있습니다.
ex) 9는 3:1:1:2를 가지는데, 3을 제외하고 뒤에서 3개 1:1:2만 보더라도 9라는걸 알 수 있습니다. (겹치지 않기 때문에)
코드
첫 번째 구현은 입력으로 들어오는 2차원 배열을 모두 저장한 후 판별을 하도록 구현했습니다.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; public class Solution { static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); static int T, N, M; static int[][] board; static StringBuilder sb = new StringBuilder(); static int[][] hcode = { {0, 0, 0, 0}, // 0 {0, 0, 0, 1}, // 1 {0, 0, 1, 0}, // 2 {0, 0, 1, 1}, // 3 {0, 1, 0, 0}, // 4 {0, 1, 0, 1}, // 5 {0, 1, 1, 0}, // 6 {0, 1, 1, 1}, // 7 {1, 0, 0, 0}, // 8 {1, 0, 0, 1}, // 9 {1, 0, 1, 0}, // A {1, 0, 1, 1}, // B {1, 1, 0, 0}, // C {1, 1, 0, 1}, // D {1, 1, 1, 0}, // E {1, 1, 1, 1}, // F }; static int[][][] rate = new int[5][5][5]; public static void main(String[] args) throws IOException { T = Integer.parseInt(br.readLine()); for (int tc = 1; tc <= T; tc++) { StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); board = new int[N][M * 4]; for (int i = 0; i < N; i++) { String str = br.readLine(); for (int j = 0; j < M; j++) { char c = str.charAt(j); int num = Character.digit(c, 16); for (int k = 0; k < 4; k++) { board[i][j * 4 + k] = hcode[num][k]; } } } init(); int ans = 0, codeLen = 7; int[] codes = new int[8]; // 암호코드 8개의 숫자를 담을 공간. for (int i = 1; i < N; i++) { for (int j = M * 4 - 1; j >= 0; j--) { if (board[i][j] == 1 && board[i - 1][j] == 0) { // 암호코드 우측 최상단 int x, y, z; x = y = z = 0; while (board[i][j] == 1) { z++; j--; } while (board[i][j] == 0) { y++; j--; } while (board[i][j] == 1) { x++; j--; } if (codeLen != 0) { while (board[i][j] == 0) { j--; } } j++; int min = Math.min(Math.min(x, y), z); // 1 비율의 값을 구한다. x /= min; y /= min; z /= min; // 암호코드의 비율을 1배수로 만든다. codes[codeLen--] = rate[x][y][z]; if (codeLen == -1) { int checkVal = (codes[0] + codes[2] + codes[4] + codes[6]) * 3 + codes[1] + codes[3] + codes[5] + codes[7]; if (checkVal % 10 == 0) { for (int item : codes) ans += item; } codeLen = 7; } } } } sb.append("#").append(tc).append(" ").append(ans).append("\n"); } System.out.println(sb); } private static void init() { for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) for (int k = 0; k < 5; k++) rate[i][j][k] = -1; rate[2][1][1] = 0; rate[2][2][1] = 1; rate[1][2][2] = 2; rate[4][1][1] = 3; rate[1][3][2] = 4; rate[2][3][1] = 5; rate[1][1][4] = 6; rate[3][1][2] = 7; rate[2][1][3] = 8; rate[1][1][2] = 9; } }
두 번째 구현은 입력으로 들어온 2차원 배열을 별도로 저장하지 않고, 행 단위로 바로바로 처리하도록 구현했습니다.
(첫 번째 방법보다 메모리 절약과 시간이 조금 더 빠릅니다.)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer; public class Solution { static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); static int T, N, M; static StringBuilder sb = new StringBuilder(); static int[][] hcode = { {0, 0, 0, 0}, // 0 {0, 0, 0, 1}, // 1 {0, 0, 1, 0}, // 2 {0, 0, 1, 1}, // 3 {0, 1, 0, 0}, // 4 {0, 1, 0, 1}, // 5 {0, 1, 1, 0}, // 6 {0, 1, 1, 1}, // 7 {1, 0, 0, 0}, // 8 {1, 0, 0, 1}, // 9 {1, 0, 1, 0}, // A {1, 0, 1, 1}, // B {1, 1, 0, 0}, // C {1, 1, 0, 1}, // D {1, 1, 1, 0}, // E {1, 1, 1, 1}, // F }; static int[][][] rate = new int[5][5][5]; public static void main(String[] args) throws IOException { T = Integer.parseInt(br.readLine()); for (int tc = 1; tc <= T; tc++) { StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); init(); int ans = 0, codeLen = 7; int[] codes = new int[8]; // 암호코드 8개의 숫자를 담을 공간. String prev = br.readLine(); int[] binaryRow = new int[M * 4]; int[] prevBinaryRow = new int[M * 4]; for (int i = 1; i < N; i++) { String row = br.readLine(); if (prev.equals(row)) continue; prev = row; // 16진수로 되어있는 행 --> 2진수 행으로 변환하기. for (int j = 0; j < M; j++) { int num = Character.digit(row.charAt(j), 16); for (int k = 0; k < 4; k++) { binaryRow[j * 4 + k] = hcode[num][k]; } } for (int j = M * 4 - 1; j >= 0; j--) { if (binaryRow[j] == 1 && prevBinaryRow[j] == 0) { // 암호코드 우측 최상단 int x, y, z; x = y = z = 0; while (binaryRow[j] == 1) { z++; j--; } while (binaryRow[j] == 0) { y++; j--; } while (binaryRow[j] == 1) { x++; j--; } if (codeLen != 0) { while (binaryRow[j] == 0) { j--; } } j++; int min = Math.min(Math.min(x, y), z); // 1 비율의 값을 구한다. x /= min; y /= min; z /= min; // 암호코드의 비율을 1배수로 만든다. codes[codeLen--] = rate[x][y][z]; if (codeLen == -1) { int checkVal = (codes[0] + codes[2] + codes[4] + codes[6]) * 3 + codes[1] + codes[3] + codes[5] + codes[7]; if (checkVal % 10 == 0) { for (int item : codes) ans += item; } codeLen = 7; } } } prevBinaryRow = binaryRow.clone(); } sb.append("#").append(tc).append(" ").append(ans).append("\n"); } System.out.println(sb); } private static void init() { for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) for (int k = 0; k < 5; k++) rate[i][j][k] = -1; rate[2][1][1] = 0; rate[2][2][1] = 1; rate[1][2][2] = 2; rate[4][1][1] = 3; rate[1][3][2] = 4; rate[2][3][1] = 5; rate[1][1][4] = 6; rate[3][1][2] = 7; rate[2][1][3] = 8; rate[1][1][2] = 9; } }
'Algorithm > SWEA' 카테고리의 다른 글
[SWEA 1248 : JAVA] 공통조상 (0) | 2021.09.01 |
---|---|
[SWEA 1232 : JAVA] 사칙연산 / 이진 트리 (0) | 2021.08.01 |
[SWEA 1231 : JAVA] 중위순회 / 완전 이진 트리 (0) | 2021.07.30 |
댓글
이 글 공유하기
다른 글
-
[SWEA 1248 : JAVA] 공통조상
[SWEA 1248 : JAVA] 공통조상
2021.09.01 -
[SWEA 1232 : JAVA] 사칙연산 / 이진 트리
[SWEA 1232 : JAVA] 사칙연산 / 이진 트리
2021.08.01 -
[SWEA 1231 : JAVA] 중위순회 / 완전 이진 트리
[SWEA 1231 : JAVA] 중위순회 / 완전 이진 트리
2021.07.30
댓글을 사용할 수 없습니다.