PS

Regex를 이용한 코테 예시

Recfli 2024. 1. 17. 22:12

[ 작성 이유 ]

 앞에선 Regex사용방법을 배웠으니 이제 Regex를 실제로 필요한 상황에 적용 시켜보자.

[ 카카오 2018년 다트게임 ]

 카카오 2018년 다트 게임에서는 총 3번의 다트를 던질 기회가 있으며 각각의 경우 0-10점을 얻을 수 있다. 점수와 관련해서 영역이 있는데 Single(S)은 1제곱, Double(D)는 2제곱, Triple(T)는 3제곱으로 점수를 계산한다. 또한 스타상(*)과 아차상(#)이 있는데 스타상은 해당 점수와 이전 점수를 2배로, 아차상은 해당 점수만큼 마이너스 시킨다. 이들 정보로 구성된 문자열이 들어왔을 때 점수 결과를 출력해라.

 

입력 예시

1.        1S2D*3T            37        1^1*2 + 2^2*2 + 3^3

2.        1D2S#10S           9        1^2+2^1(-1) + 10^1

3.        1D2S0T               3        1^2 + 2^1 + 0^3

 

해당 문제를 잘 풀기 위해서는 어차피 기회가 3번인거 파싱만 깔끔하게 하면 문제가 굉장히 쉬워진다. 그런데 파싱을 어떻게 할 지가 곤란하다. 1번은 [1S, 2D*, 3T], 2번은 [1D,2S#,10S], 3번은 [1D,2S,0T] 이렇게 파싱되면 참 편할 것 같다.

 

바로 생각나는 데로 임의로 만들어봤는데 생각보다 시간이 오래 걸리고 코드도 길다. 

def parse(dartResult):
    token = []
    
    ptr = 0
    for i in range(3):
        num = ""
        cal = ""
        prize = ""
        while True:
            if(ptr == len(dartResult)):
                break
            
            if(ord(dartResult[ptr]) >= ord('0') and ord(dartResult[ptr]) <= ord('9')):
                if(len(cal) != 0):
                    break
                num = num + dartResult[ptr]
                ptr += 1

            elif(dartResult[ptr] == 'S' or dartResult[ptr] == 'D' or dartResult[ptr] == 'T'):
                cal = cal + dartResult[ptr]
                ptr += 1
            else:
                prize = prize + dartResult[ptr]
                ptr += 1
                break
        token.append(num+cal+prize)
    return token

 

이걸 이제 Regex를 이용해서 풀어보자. 우선 숫자와 관련해서는 0부터 10까지 무조건 1개 등장할 수 있고 이후에는 S, D, T가 무조건 1개 등장한 뒤 #이나 *는 등장할 수도 있고 안할 수도 있다.

pattern = re.compile(r'([0-9]|10)([SDT])(\*\#)?')
print(pattern.findall('1S2D*3T'))

 

이렇게 단 두 줄이면 위에서 한 고생을 안해도 된다. 해당 부분이 3파트로 나뉘어져있으니 for문을 돌면서 내부용 변수 3개를 위에 내가 설정한 것처럼 num, cal, prize 이렇게 두고 나머지 계산 부분만 하면 동일하다.

 

[ 카카오 2018년 뉴스 클러스터링 ]

 두 문자열 "FRANCE"와 "FRENCH"가 주어졌을 때 두 글자씩 끊어서 {FR, RA, AN, NC, CE}, {FR, RE, EN, NC, CH}로 만들어서 문제 조건에 따라서 검사를 해야한다. 그런데 들어오는 문자열은 대소문자 구분이 없고 두글자씩 끊었을 때 영어가 아닌 문자가 들어가면 집합에 포함시키지 않는다.

 

 그렇기 때문에 저렇게 리스트 구성요소를 만들기만 한다면 매우 쉽겠지만 문제는 반복문으로 슬라이싱을 해도 저게 맞는지 아닌지 조건 검사하기가 까다롭다. 그 때 정규식을 사용하면 편하다. 정규식은 매우 간단하다 어차피 난 문자열 모두 다 LowerCase로 바꿔버릴거고 '[a-z]{2}'만 만족하면 원하는데로 끊을 수 있다. 그래서 다음과 같이 파싱을 하였다.

def solution(str1, str2):
    str1 = str1.lower()
    str2 = str2.lower()
    
    str1List = []
    str2List = []

    pattern = re.compile(r'[a-z]{2}')
    for i in range(len(str1)-1):
        stream = str1[i] + str1[i+1]
        if pattern.findall(stream):
            str1List.append(stream)

    for i in range(len(str2)-1):
        stream = str2[i] + str2[i+1]
        if pattern.findall(stream):
            str2List.append(stream)

 

 이렇게 하면 원하는 Regex 규칙에 따라서 해당 string이 포함되는지를 상대적으로 간단하게 확인할 수 있다. 물론 a-z까지 그냥 배열에 26짜리로 넣어서 순환하는 함수를 만들어도 되긴하지만 개인적으로 이 방법도 익혀두고 싶었다.

[ 정리 ]

 앞선 글에서 Regex를 이용해서 문자열에서 원하는 패턴을 찾아 파싱하는 방법을 익혔다. 또한 이 글에서는 그걸 이용한 코테 문제 두 개로 단순히 문자열 내부에서 파싱만 해도 되는 경우, 중간에 고정된 길이의 겹쳐있는 것을 찾아야하는 경우 어떻게 처리할지를 확인했다. 이것보다 한 단계 더 진화한 경우로 글자수가 정해져있지 않고 겹치게 찾아야 하는 경우는 문제가 있다면 나중에 고민을 해보고 올리겠다.

[ 참고 자료 ]

https://www.youtube.com/watch?v=orf9ailzXvI