[SWEA] 1240. 단순 2진 암호코드

new Dean( );·2021년 8월 4일
0

알고리즘

목록 보기
15/30

문제

1240. [S/W 문제해결 응용] 1일차 - 단순 2진 암호코드

배열 중간에 암호코드가 등장한다. 이 암호코드는 7자리씩 총 8개의 숫자로 이루어져있다. 이때, 마지막 숫자는 검증코드인데 이 검증코드가 유효한지 정해진 규칙에 따라 확인한다. 유효하다면 각 자리 숫자의 합을 출력하고, 유효하지 않다면 0을 출력한다.

주의할 점

각 숫자는 7bit의 이진수로 주어진다. 그런데 단순히 2진수->10진수로 변환시켜 사용하는 것이 아니고, 주어진 그림을 활용해서 일치하는 숫자를 찾아내야한다.

  • 비트연산을 문제에서 처음 활용하는 데다가 문제가 이해하기 어려워서 어질어질했다.

풀이

  1. 그림으로 표시된 숫자를 활용해서 각 숫자의 값을 배열에 저장한다. initNumber()

  2. 입력받는다. String으로 한 줄씩 입력받았다. input()
    2-1. 1이 나오면 암호코드의 시작으로 이해하고 최대 56자리까지 확인해서 input이라는 문자열에 저장했다. (지금보니 함수병이랑 변수명이 같네.. 무슨 일ㅜㅜ)
    2-2. 암호코드를 저장한 다음에는 isDone=true; 로 표시해주고, 남은 입력은 형식상 받아줬다. 배열로 다 받은 다음 암호코드를 찾을 수도 있지만 메모리낭비같아서 이렇게 했다.

  3. 암호코드를 String에 저장했으므로 비트연산의 shift연산 느낌을 내기 위해 main함수에서 for문을 돌렸다.
    3-1. 암호코드의 앞 또는 뒤에 0이 더 붙어있다. 그런데 앞에 붙었는지 뒤에 붙었는지 모르기 때문에 일단 맨 앞이 1인 것으로 시작하고, 맨뒤에 남은 0의 개수만큼 오른쪽으로 한칸씩 shift하는 효과를 냈다.
    3-2. 7번 반복 : 가장 많이 shift하는 경우가 1000000 ~ 0000001 이기 때문에 최대 7번
    3-3. answer > 0 이면 검증코드가 유효하다는 것 ! 바로 출력하면 된다.

    함수명설명
    setPassword(i)i가 오른쪽으로 몇 칸 밀리는지를 알려준다. shift한 암호코드를 배열에 갱신한다.
    getCode()검증코드의 유효성을 검사한다.
    getRealNum비트연산자 ^ 를 이용해서 서로 일치하면 해당되는 숫자를 반환한다.
    (지금보니 if (password == number[i])로 해도 되는데 ㅎㅎ.. 비트연산자가 써보고 싶었던 걸로~)

자바코드

import java.util.Scanner;
import java.io.FileInputStream;

class Solution
{	
	static String input ="";
	static int [] passwords = new int[8];
	static int [] number = new int[10];
	public static void main(String args[]) throws Exception
	{
		initNumber();
		
		System.setIn(new FileInputStream("res/input (23).txt"));
		Scanner sc = new Scanner(System.in);
		int T = sc.nextInt();
		sc.nextLine();
		
		for(int test_case = 1; test_case <= T; test_case++)
		{
			input(sc);
			
			int answer = 0;
			for (int i=0; i<7; i++) {
				setPassword(i);
				answer = getCode();
				if (answer > 0) break;
				if (input.charAt(55-i) == '1') break; // 끝까지 다 밀었다.
			}
			
			System.out.printf("#%d %d\n", test_case, answer);
		}
	}
	
	/**
	 * 암호코드를 구성하는 숫자 설정 
	 */
	static void initNumber() {
		number[0] = Integer.parseInt("0001101", 2);
		number[1] = Integer.parseInt("0011001", 2);
		number[2] = Integer.parseInt("0010011", 2);
		number[3] = Integer.parseInt("0111101", 2);
		number[4] = Integer.parseInt("0100011", 2);
		number[5] = Integer.parseInt("0110001", 2);
		number[6] = Integer.parseInt("0101111", 2);
		number[7] = Integer.parseInt("0111011", 2);
		number[8] = Integer.parseInt("0110111", 2);
		number[9] = Integer.parseInt("0001011", 2);
	}
	
	static void input(Scanner sc) {
		input = "";
		boolean isDone = false; // 암호코드 찾았는지 여부 

		int r = sc.nextInt();
		int c = sc.nextInt();
		sc.nextLine();
		
		for (int i=0; i<r; i++) {
			String str = sc.nextLine();
			
			if (isDone) continue; // 이미 암호코드를 저장했음.
			
			// 암호코드를 찾으러 가는 여정 
			for (int j=0; j<c; j++) {
				if (str.charAt(j) == '0') continue;

				// 첫 1 등장 == 암호코드 등장 
				for (int size = 0; size<56; size++) {
					if ((j+size)>=c) break; // out of range
					input += str.charAt(j+size);
				}
				isDone = true;
				break;
			}
		}
	}
	
	/**
	 * number[]과 비교하여 문제에서 주어진 숫자로 변환한다.
	 * @param password 코드의 8개의 숫자 중 현재 확인할 숫
	 * @return 변환된 숫자 (0~9)
	 */
	static int getRealNum(int password) {
		for (int i=0; i<10; i++) {
			if ((password^number[i]) == 0) return i;
		}
		return -1;
	}
	
	/**
	 * 가능한 암호를 passwords 배열에 갱신해준다.
	 * @param turn 몇 번째인지를 받아서 shift 효과를 냄 
	 */
	static void setPassword(int turn) {
		String str;
		int start = 7-turn;
		str = input.substring(0, start);
		passwords[0] = getRealNum(Integer.parseInt(str, 2));
		for (int i=0; i<7; i++) {
			str = input.substring(start, start+7);
			passwords[i+1] = getRealNum(Integer.parseInt(str, 2));
			start += 7;
		}
	}
	
	/**
	 * 검증코드가 유효한지 확인함.
	 * @return 유효하면 각 자리의 수를 합한 결과 / 유효하지 않으면 0
	 */
	static int getCode() {
		int answer = 0;
		answer = (passwords[0]+passwords[2]+passwords[4]+passwords[6])*3 + (passwords[1]+passwords[3]+passwords[5]) + passwords[7];
		if (answer%10 != 0) return 0; // 유효하지 않음 
		
		answer = 0;
		for (int i=0; i<8; i++) answer += passwords[i];
		return answer; // 합한 결과 
	}
}

비고

되게 복잡하게 푼 것 같다.
간단하게 설명하는 능력을 키우고 싶다.

다음에 한다면 (210805 추가)

다음 이미지와 같이 모든 2진수 숫자가 1로 끝난다. 따라서 풀이에서 3번(shift느낌)이 필요가 없었다! 그냥 뒤에서 첫 번째로 나오는 1을 암호코드의 끝으로 맞추면 된다.

0개의 댓글