알고리즘/알고리즘_swea

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

Jerry_K 2024. 5. 7. 13:02
 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com


🗒️ 파이썬 코드 풀이

#--------------------------------------------------------
# 아이디어 정리
# 0. 2진 코드 딕셔너리 형태로 생성
# 1. 행의 합이 0인것 제외하고 0이 아닌 첫 행 추출 (for-if)
# 2. 뒤에서 부터 1이 나오는 인덱스 위치부터 -56개까지 추출 (for)
# 3. 56비트를 7비트씩 8개로 쪼개고, 딕셔너리에서 value값 추출 (for)
# 4. 올바른 코드인지 체크 (for)
#--------------------------------------------------------
# 시간 복잡도 
# 다중 for문이 없으므로 큰 문제 없음
#--------------------------------------------------------
# 자료구조 
# num_dic : 2코드 딕셔너리
# lst : int형 배열
# code_lst : int형 배열 (lst에서 필요행 하나만 추출)
# code_str : 문자열 (암호를 해독한 숫자들을 문자열로 변환)
# odd, even : 홀수/짝수 분할
#--------------------------------------------------------

# 0 
num_dic = {
    "0001101" : "0",
    "0011001" : "1",
    "0010011" : "2",
    "0111101" : "3",
    "0100011" : "4",
    "0110001" : "5",
    "0101111" : "6",
    "0111011" : "7",
    "0110111" : "8",
    "0001011" : "9"
}

T = int(input())
for test_case in range(1, T + 1):
    N,M = list(map(int,input().split()))
    lst = [list(map(int,input())) for _ in range(N)]
    
    # 1 
    for ls in lst : 
        if sum(ls) != 0 :
            code_lst = ls
    
    # 2
    for i  in range(len(code_lst)-1,0,-1) : 
        if code_lst[i] == 1 : 
            e = i  # 뒤에서 부터 1이 나오는 첫번째 인덱스
            break 
    
    code_str = ""
    for s in range(e-56+1,e,7) : 
        # 3 
        # code_lst를 딕셔너리의 key 출력을 위해 문자열로 전환
        code_str += num_dic["".join(map(str,code_lst[s:s+7]))]
    
    # 4
    odd = code_str[0:8:2] 
    even = code_str[1:8:2] 
    o_sum=e_sum = 0
	
    for i in range(4) :
        e_sum += int(even[i])
        o_sum += int(odd[i])
    # print("64","로그남기기 예시" )
    print(f"#{test_case} ",end="")
    if ((o_sum*3) + e_sum) % 10 == 0  : 
        print(o_sum + e_sum)
    else : 
        print(0)

 

1. 2진 코드 딕셔너리 형태로 생성한다. (딕셔너리 형태로 만들어두면 key,value값을 쉽게 추출 가능)


2. 행의 합이 0인것 제외하고 0이 아닌 첫 행 추출 (코드를 해석 할 수 있는 하나의 행만 필요)


3. 뒤에서 부터 1이 나오는 인덱스 위치부터 -56개까지 추출 (범위 선택이 헷갈리니 출력을 해보면서 범위 설정)


4. 56비트를 7비트씩 8개로 쪼개고, 딕셔너리에서 value값 추출 (이 부분도 역시 범위 설정을 조심하자) 

또한 문자열과 숫자열의 자유자재로 변환 할 줄 알아야한다. (split,join 함수 활용)


5. 올바른 코드 체크도 문자열과 숫자열만 잘 전환하면 어렵지 않게 확인 할 수 있다.

 

*  코드 작성 중 , 내 코드가 잘 작성되고 있나 중간 중간 체크가 중요하다.

로그를 통한 코드 체크시 해당 줄 번호를 남겨서 좀 더 가독성을 높혀보자.

ex )  print("64(줄 번호)" , log )

 

📑 내 코드 풀이

T = int(input())
for tc in range(1,T+1) : 
    N,M = list(map(int,input().split()))
    lst = [list(map(int,input())) for _ in range(N)]
    
    # 0~9 이진 비밀 코드 입력
    num_lst = [[0,0,0,1,1,0,1],[0,0,1,1,0,0,1],[0,0,1,0,0,1,1],[0,1,1,1,1,0,1],[0,1,0,0,0,1,1],\
    [0,1,1,0,0,0,1],[0,1,0,1,1,1,1],[0,1,1,1,0,1,1],[0,1,1,0,1,1,1],[0,0,0,1,0,1,1]]
    
    # 행에 1이 있는 첫번째 행 가져오기
    for n in range(N) : 
        if sum(lst[n]) != 0 :  
            n_lst = lst[n]  # 1이 있는 첫번째 행

    secret_lst = [] # 7비트씩 8분할 한 세트 (길이 56)
    for i in range(M-56+1) : 
        secret_lst.append(n_lst[i:i+56])
    
    code_lst = []  # 암호가 해독된 code 리스트
    for ls in secret_lst : # 해독 가능한 code 리스트들만 선발
        tmp = []
        code_state = 0
        for i in range(8) :
            if ls[i*7 : i*7+7] in num_lst : 
                idx = num_lst.index(ls[i*7 : i*7+7])
                tmp.append(idx)
                code_state = 1
            elif ls[i*7 : i*7+7] not in num_lst : 
                code_state = 0
                break
        if code_state == 1 : # 0이 되는 경우 code_lst에 append 불가능
            code_lst.append(tmp)
            
    # # 짝수,홀수로 구분 후 10의 배수 확인하여 올바른 코드인지 확인
    length = len(code_lst[0])
    for i in range(len(code_lst)) : 
        sum_odd = 0
        sum_even = 0
        str_lst = list(map(str,code_lst[i]))
        for j in range(8) :
            if (j-1) % 2  == 1 : 
                sum_odd += int(str_lst[j])
            else :
                sum_even += int(str_lst[j])

    ans = sum_even + sum_odd*3
    print(f"#{tc} ",end="")
    if ans % 10 == 0 :
        print(sum_even+sum_odd)
    else : 
        print(0)

 

내가 이 문제의 조건들을 잘 사용하지 못해서, 너무 복잡한 코드를 구성했다.

 

우선 맨 뒤에 공통적으로 1이 나오는 것들 생각 못했다.  

이렇게되면, 행의 처음 시작부터 마지막까지 56길이가 되는 문자열을 하나하나 다 확인해야한다.

 

또 조건 사용을 잘 못한 것은 암호 코드가 무조건적으로 해독 될 수 있다는 부분이다.

암호코드가 해독 안될 수 있다는 가정으로 설정한 조건은 까다로웠다 .

 

1. 전체 길이를 반복해서 56비트를 8개로 나눈 2차원 리스트를 만들고,

2. 각 56개로 나눈 리스트들 중 7번 연속으로 이진 비밀 코드에 들어있는 리스트만 가져왔다.

( 한번이라도 조건이 성립하지 않으면 code_lst에 들어 갈 수 없음)

 

 

📌 생각 정리

아직 모든 문제를 풀어보지는 않았지만, 너무 복잡하다면 이상함을 감지해야한다.

빠르게 풀어야겠다는 생각으로, 가장 먼저 떠오르는 방법을 한게 문제였다.

 

코드를 작성하기전에 효율적인가를 보고, 설계를 명확하게 해야겠다.

또한 문제가 복잡해질수록 여러 변수들을 선언해야하는데, 자료 구조에 대해 잘 메모해두자.

 


📚 문제

어떤 국가에서는 자국 내 방송국에서 스파이가 활동하는 사실을 알아냈다. 스파이는 영상물에 암호 코드를 삽입하여 송출하고 있었는데, 스파이의 암호 코드에 다음과 같은 규칙이 있음을 발견했다.

1. 암호코드는 8개의 숫자로 이루어져 있다.

2. 암호코드에서의 숫자 하나는 7개의 비트로 암호화되어 주어진다. 따라서 암호코드의 가로 길이는 56이다.
   ※ 길이가 56가 아닌 코드는 주어지지 않는다. 주어진 암호코드는 주어진 규칙대로 해독할 수 있음을 보장한다.
      암호코드의 각 숫자가 암호화되는 규칙은 주어진 그림1을 참고하라.


3. 올바른 암호코드는 (홀수 자리의 합 x 3) + (짝수 자리의 합)이 10의 배수가 되어야 한다.
    ex) 암호코드가 88012346일 경우,
    ( ( 8 + 0 + 2 + 4 ) x 3 ) + ( 8 + 1 + 3 + 6) = 60은 10의 배수이므로 올바른 암호코드다.
    ex) 암호코드가 19960409일 경우,
    ( ( 1 + 9 + 0 + 0 ) x 3 ) + ( 9 + 6 + 4 + 9) = 58은 10의 배수가 아니므로 잘못된 암호코드다.




이 암호코드들을 빠르고 정확하게 인식할 수 있는 스캐너를 개발하려고 한다.
스캐너는 암호코드 1개가 포함된 직사각형 배열을 읽는다.
직사각형 배열은 1, 0으로만 이루어져 있고, 암호코드 이외의 부분은 전부 0으로 주어진다.
암호코드 정보가 포함된 2차원 배열을 입력으로 받아 올바른 암호코드인지 판별하는 프로그램을 작성하라.


[입력]

가장 첫줄은 전체 테스트 케이스의 수이다.

각 테스트 케이스의 첫 줄에 두 자연수가 주어지는데 각각 배열의 세로 크기 N, 배열의 가로크기 M이다 (1≤N≤50, 56≤M≤100).

그 다음 N개의 줄에 걸쳐 N x M 크기의 직사각형 배열이 주어진다.

[출력]

각 테스트 케이스의 답을 순서대로 표준출력으로 출력하며, 각 케이스마다 줄의 시작에 “#C”를 출력하여야 한다. 이때 C는 케이스의 번호이다.

주어진 암호코드가 올바른 암호코드일 경우, 암호코드에 포함된 숫자의 합을 출력하라. 만약 잘못된 암호코드일 경우 대신 0을 출력하라.

[예제 풀이]

첫 번째 케이스의 암호코드 정보를 추출하면 아래와 같다.

01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
01110110110001011101101100010110001000110100100110111011
 
이 숫자가 나타내는 정보는 각각 아래와 같다.
0111011(7) 0110001(5) 0111011(7) 0110001(5) 0110001(5) 0001101(0) 0010011(2) 0111011(7)
 
검증코드가 맞는지 살펴보면, (7 + 7 + 5 + 2) * 3 + 5 + 5 + 0 + 7 = 80 이므로 올바른 암호코드라고 할 수 있다. 따라서 1번의 출력 값은 38이 된다.
 
두 번째 케이스도 같은 방식으로 계산할 경우, 잘못된 암호코드임을 알 수 있다. 따라서 출력 값은 0이 된다.