[SWEA][D5][#01242] 암호코드 스캔

작성:    

업데이트:

카테고리:

태그: , ,

출처

학습용 포스트입니다. 본 포스트가 문제가 될 시 수정 또는 삭제하겠습니다.


문제

문제 생략


My Sol

dictA = {
    '0': '0000',
    '1': '0001',
    '2': '0010',
    '3': '0011',
    '4': '0100',
    '5': '0101',
    '6': '0110',
    '7': '0111',
    '8': '1000',
    '9': '1001',
    'A': '1010',
    'B': '1011',
    'C': '1100',
    'D': '1101',
    'E': '1110',
    'F': '1111'
}

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

# 메인 함수
def main():
    for i in range(H):
        if mat[i].rstrip('0'):
            subMain(i, mat[i])
            i -= 1

# 서브메인 함수
def subMain(i, arr):
    lastJ = checkLastIdx(arr)
    txt = convertH2B(i, lastJ)
    thk = calcThick(txt)
    findPW(txt, thk)
    removeUsed(i, lastJ, thk)

# arr의 마지막 데이터 인덱스를 찾는 함수
def checkLastIdx(arr):
    return len(arr.rstrip('0'))-1

# 16진수를 2진수로 바꾸는 함수
def convertH2B(startI, lastJ):
    txt = ''
    j = lastJ
    while j >= 0:
        txt = dictA[mat[startI][j]] + txt
        j -= 1
    return txt.rstrip('0')

# 두께 계산 함수
def calcThick(txt):
    tl = len(txt)
    arr = []
    al = 0

    k = tl-1
    flag = 0
    while True:
        # 다른 숫자
        if not flag==txt[k]:
            if al==4: break
            flag = txt[k]
            arr.append(1)
            al += 1

        # 같은 숫자
        else:
            arr[-1] += 1
        k -= 1
    return sum(arr)//7

# 암호 문자 변환
def findPW(txt, thk):
    global PWLst
    l = 56*thk
    txt2 = txt[-l:]
    mainTxt = ''
    for k in range(8):
        di = k*thk*7
        subTxt = ''
        for i in range(l-1-k*thk*7, l-1-(k+1)*thk*7, -thk):
            subTxt = txt2[i] + subTxt
        mainTxt = dictP[subTxt] + mainTxt
    PWLst.append(mainTxt)

# 확인한 암호 mat에서 제거
def removeUsed(startI, lastJ, thk):
    L = thk*14

    if not dictA[mat[startI][lastJ]][3]:
        L += 1

    i = startI
    tool = mat[i][lastJ-L:lastJ+1]
    while tool == mat[i][lastJ-L:lastJ+1]:
        mat[i] = list(mat[i])
        mat[i][lastJ-L:lastJ+1] = ['0']*(L+1)
        mat[i] = ''.join(mat[i])
        i += 1

# PWLst 답 확인
def checkPW(pw):
    sumodd = sumeven = 0
    for i in range(8):
        if i&1:
            sumodd += int(pw[i])
        else:
            sumeven += int(pw[i])
    return 0 if (sumeven*3+sumodd)%10 else sumodd+sumeven


T = int(input())
for tc in range(1, T+1):
    H, W = map(int, input().strip().split())
    mat = [input().strip() for _ in range(H)]
    PWLst = []
    main()

    ans = 0
    for pw in PWLst:
        ans += checkPW(pw)
    print(f'#{tc} {ans}')

작성했던 PS 답안 중 가장 긴 코드 길이였다. 다양한 기능을 하는 함수들이 필요했기 때문에 절차를 컴포넌트화하여 오류 디버깅이 쉽게 작성하였다.

rstrip을 이용해 오른쪽에 0을 모두 제거하는데, 만약 암호코드가 없는 라인이라면 모두 0이므로 빈 리스트가 된다. 때문에 처음으로 빈 리스트가 아닌 라인을 만나면 해당하는 라인의 오른쪽부터 암호를 찾으면 되겠다.

같은 라인에 2개의 암호코드가 존재할 수 있기 때문에 한 코드를 분석하고 나면 mat에서 이 코드와 연결된 하위 라인 모두 0으로 바꿔주고 다시 해당 라인부터 코드를 탐색할 것이다. 이를 위해 각 라인을 조회하는 for문에 암호를 발견하면 이 암호로부터 수를 계산해내고 다시 인덱스를 빼주어 해당 라인을 재조회할 수 있도록 하였다.

해당 라인의 암호 길이는 두께*56으로 계산할 수 있는데, 이는 2진수이고, 문제는 16진수이므로 끝 인덱스부터 처음까지 16진수를 모두 2진수로 변환한다. 이 과정에서 딕셔너리를 사용하면 편하게 할 수 있다.

16진수이므로 2진수로 변환하면 끝이 다시 0이 될 수도 있는데 이를 rstrip을 이용해 다시 제거해 본격적으로 비율에 대해서 확인해본다. 4번 값이 바뀌는 동안의 길이를 계산해 7로 나눈 몫이 두께가 된다. 이 두께를 이용해 2진수의 암호 길이를 결정한다.

2진수의 암호 길이로 조회 텍스트를 슬라이싱하여 추출한 뒤, 두께 단위로 건너뛰면서 7개의 2진수를 pick해 딕셔너리를 이용해 암호코드로 변환한다. 이 과정을 8개를 진행하여 수를 만들면, 외부의 PWLst에 보내고 해당 암호에 대한 조회를 종료한다.

하위에는 해당 암호와 같은 코드들이 계속 있을 것이기 때문에 슬라이싱을 이용하여 해당 암호와 같다면 0으로 바꿔주는 함수를 작성한다. 이때 string 자체는 immutable하여 중간만 슬라이싱으로 바꿔줄 수 없기 때문에 하위의 구간이 상위의 조회했던 구간과 값이 같다면 하위 라인을 모두 리스트로 처리한 뒤 0으로 바꾸고 다시 join하여 string으로 만들어준다. 처음부터 리스트로 처리한다면 rstrip을 사용할 수 없기 때문에 불가피한 과정이다.

조회 코드의 왼쪽에 다른 코드가 있을 수 있으므로 조회한 블럭은 0으로 바꾸고 다시 subMain()을 해당라인부터 확인하여 rstrip으로 0이 아닌 라인을 계속 확인한다. 이 과정을 끝까지 하면 PWLst에 숫자들이 쌓일 것이다. 이 숫자들을 확인하여 값을 더해주고 출력하면 되겠다.

주의해야 할 점은 조회한 코드를 제거할 때 몇 개나 제거해야 하는지가 관건인데, 2진수 암호 코드는 끝이 1부터 시작하므로 끝이 0인 암호라면 앞에 16진수 문자를 하나 더 확인해야 하는 것이다. 이를 가장 좌측 인덱스의 16진수의 2진수 변환 값을 확인하여 가장 오른쪽 값이 1이면 해당 16진수의 4비트를 모두 사용하고, 0이라면 4비트를 모두 사용하지 않고 16진수 길이를 하나 더 추가해 맨 앞의 16진수 문자도 제거 대상으로 포함한다.


결과

PASS

댓글남기기