본문 바로가기
언리얼_엔진_게임개발_공부/알고리즘&자료구조

[코드카타] "내 코드 쓰레기네" ... 과연 그럴까?

by jaboy 2025. 1. 17.

프로그래머스 스쿨의 코딩테스트 연습문제를 풀어 제출해 정답을 맞춘 후 다른 사람의 풀이를 확인하는데...

충격적으로 명료한 어떤 사람의 풀이가 나의 장황한 풀이와 너무 대비가 되었다...

아래의 댓글이 나의 충격을 대신 말해주고 있었다.

그러게... 마리오... 내 코드도...

내가 풀이한 문제

https://school.programmers.co.kr/learn/courses/30/lessons/133499

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

나는 아래와 같이 생각했다.

  • Loop 1. babbling 에 저장된 각 string 에 대하여:
    • Loop 2. 2, 3글자 단위로 발음 가능한지 확인
      • 확인할 글자가 더 이상 남지 않았으면 발음 가능. Exit Loop
      • 1글자만 남았으면 발음 불가능. Exit Loop
      • 첫 2글자가 유효한 발음인지 체크
        - 유효 조건 : 발음할 수 있는지 && 마지막 발음과 다른지
           --> substr(2) 으로 해당 2글자를 제외한 스트링에 대해 다시 Loop
        - 유효하지 않으며, 마지막 2글자였다면 발음 불가, Exit Loop
      • 첫 3글자도 동일하게 체크
        - 유효하지 않았으면 발음 불가 Exit Loop

지금까지 코딩테스트 연습문제를 풀며 관찰한 나의 습관? 같은 게 있다면...

스트링이나 컨테이너를 순환하며 '유효한가' 체크할 때, 판단을 최대한 빨리 하고 반복문을 빠져나오게 코드를 쓰는 습관이 있는 것 같다.

(최대한 일찍 loop 를 빠져나올 수 있도록 하는 것에 신경을 쓰는 습관이랄까...? 조건문 체크를 몇 차례 더 하더라도...)

(...코드도 성질 급한 나의 성미를 반영하나보다...)

 

아무튼 위의 로직을 아래와 같이 코드로 작성했다.

#include <string>
#include <vector>

using namespace std;

int solution(vector<string> babbling) {
    int answer = 0;
        
    for (string s : babbling) {
        string temp = s;
        string last = ""; // 마지막으로 일치한 발음
        bool flag = false; // 발음 가능 여부
        
        while (true) {
            // 모든 글자가 유효했다면 발음 가능 - Set Flag & Exit Loop
            if (temp.empty()) {
                flag = true;
                break;
            }
        
            // 1 글자 남았으면 발음 불가 - Exit Loop
            if (temp.length() == 1) break;
            
            // 첫 2글자와 비교
            string FirstTwo = temp.substr(0, 2);
            
            // 마지막 발음과 같으면 발음 불가 - Exit Loop
            if (FirstTwo == last) break;
            
            // 발음할 수 있는지 체크
            if (FirstTwo == "ye") {
                temp = temp.substr(2); // 확인된 2글자 제외
                last = "ye"; // 마지막 일치한 발음 업데이트
                continue;
            }
            else if (FirstTwo == "ma") {
                temp = temp.substr(2);
                last = "ma";
                continue;
            }
            
            // 마지막 2글자를 확인했다면 발음 불가 - Exit Loop
            if (temp.length() == 2) break;
                        
            // 첫 3글자와 비교
            string FirstThree = temp.substr(0, 3);
            
            // 위와 동일
            if (FirstThree == last) break;
            
            if (FirstThree == "aya") {
                temp = temp.substr(3);
                last = "aya";
                continue;
            } else if (FirstThree == "woo") {
                temp = temp.substr(3);
                last = "woo";
                continue;
            }
            
            // 전부 일치하지 않았음
            break;
        }
        // 발음 가능 flag 확인하여 카운트 증가
        if (flag) answer++;
    }
    
    return answer;
}

딱 보기에도 장황하다...

 

 

다른 사람의 풀이

여느 때처럼 다른 사람의 풀이와 비교하기 위해 확인하는데, 충격적으로 명료하다!

#include <string>
#include <vector>

using namespace std;

int solution(vector<string> babbling) {
    int answer = 0;

    for(int i; i < babbling.size(); i++)
    {
        string temp1="";
        string temp2="";
		for(char c : babbling[i])
        {
            temp1+=c; // 발음 가능한지 확인할 스트링에 현재 character 추가
            
            // 발음 가능한 경우
            if(temp1 == "aya"||temp1 == "ye"||temp1 == "woo"||temp1 == "ma")
            {
            	// 마지막 발음과 동일한 경우 발음 불가 --> exit loop
                if(temp2 == temp1) break;
                // 마지막 일치한 발음 저장
                temp2=temp1;
                // 테스트할 스트링 비움
                temp1="";
            }
        }
        
        // 마지막 발음과 동일하여 반복문을 빠져나오면,
        // temp1 에 character 가 추가된 상태로 반복문을 빠져나오므로
        // temp1 이 비었을 때만 유효한 발음
        if(temp1.size()==0) answer++;
    }
    return answer;
}

글자 단위로 체크하기 때문에 조건 체크가 적고 코드 자체가 간단 명료하다.

- nested loop 안에서 조건체크가 1~2회 있다.

- 무조건 글자 수만큼 character 추가와 조건 체크를 실행한다.

 

비교해보자면, 내 코드는

- nested loop 안에 조건 체크가 9회 있다.

- 2~3글자씩 advance하며, 발음 불가가 판단되자마자 반복문을 빠져나온다.

 

만일 테스트할 스트링이 100글자라면,

내 코드는 안쪽 반복문을 best case 1번, worst case 50번 (yemayemayema... --> 2글자씩 50번) 수행하고,

위 코드는 안쪽 반복문을 무조건 100번 수행한다.

 

그래서 테스트 케이스 결과를 비교하고 싶어졌다. 뭐가 더 빠른지...

내가 작성한 코드 / 다른 사람의 풀이

 

근소한 차이이긴 하지만, 2번과 4번 케이스 (0.02 vs 0.01) 를 제외하고 모든 케이스에서 내 코드가 더 짧은 시간이 걸린다.

최대로 차이 나는 케이스들은

0.03 vs 0.08

0.02 vs 0.07 

 

스트링의 길이가 길어질 수록 차이가 더 나게 되지 않을까 생각한다.

 

무엇이 달랐을까?

나는 유효 케이스 스트링이 2 혹은 3글자라는 점에서 착안하여 코드를 작성했다는 것이 근본적인 차이였던 것 같다.

그래서 나의 코드는 이 연습문제에서 제시한 발음들에 한정된 풀이이고, 저 사람의 풀이는 어떤 발음이 오든 상관없이 유효한 풀이인 것이다.

만약 문제가 조금 달라졌다면, 나는 저런 풀이를 생각할 수 있었을까?

2~3글자라는 공통점이 없었다면 아마 한글자씩 체크하려고 하지 않았을까 생각한다.

물론 유효한 스트링이 아니라면 최대한 일찍 loop 를 벗어나는 방법을 고민했겠지...

 

그래서...글쎄...내 코드가 마냥 쓰레기가 아니었을 수도 있겠다는 생각을 했다...