본문 바로가기
TIL

TIL 240523 | 알고리즘 코드카타 리뷰 - 옹알이(2)

by lemonpie611 2024. 5. 23.

1. 문제

문제 링크

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

그러니까, 문자열로 이루어진 배열이 주어졌을 때, 그 중에서 조건에 맞는 배열의 개수를 반환하는 것이다.

  • 문자열은 "aya", "ye", "woo", "ma" 4가지 문자열로만 이루어져 있어야 한다.
  • 위에서 언급된 4가지 문자열 중 하나의 문자열이 연속해서 나타나면 안된다.

ex)

["aya", "yee", "u", "maa"] > 조건에 맞는 문자열은 "aya" 1개 > 1 반환

["ayaye", "uuu", "yeye", "yemawoo", "ayaayaa"] > 조건에 맞는 문자열은 "ayaye", "yemawoo" 2개 > 2 반환

 

2. 접근 방법

  1. 배열 내 요소들을 순회하며 조건에 맞는지 여부를 체크하고, 조건에 맞으면 cnt를 1씩 증가시킨다.
  2. 배열 요소인 문자열에 대해, 조건에서 주어진 문자열로 시작하는지 확인 > 해당 문자열은 잘라내기
  3. 2를 반복하여 문자열에 아무것도 남지 않으면 cnt에 1을 더해 반환

 

근데 이 방법을 그대로 쓰려니 반복문에서 꼬이게 됐다. 주어진 문자열과 비교하는 문자열 모두 여러개인데다가, 하나의 문자열에 대해서도 여러번 검사를 해야하기 때문에 중첩 for문을 여러개 써야 하는 단점이 있었다.

 

그래서 다음으로 생각한게 재귀함수이다.. 주어진 배열에 대해 reduce 메서드를 쓰고, reduce 안에 sols 재귀함수를 실행하도록 구현하였다.

 

  1. 주어진 문자열 cur에 대해, 조건에서 주어진 문자열(babs)로 시작한는지 확인 > 해당 문자열(cur)을 잘라내 cur2 생성
  2. babs로 시작 && (cur 잘라낸 cur2에 문자열도 babs로 시작 or cur2에 아무것도 남지 않았다) > 재귀함수 호출
  3. cur에 아무것도 남지 않았다 > 1을 반환
  4. 문자열이 조건에 맞지 않는다 > 0을 반환
  5. reduce 메서드를 이용하여 acc에 함수의 반환값만큼 더함
function solution(babbling) {
  let babs = ["aya", "ye", "woo", "ma"];

  let sols = function (babs, cur) {
    if (cur.length == 0) {
      return 1;
    }
    for (let bab of babs) {
      let cur2 = cur.slice(bab.length, cur.length);
      if (
        cur.indexOf(bab) == 0 &&
        (cur2.indexOf(bab) != 0 || cur2.length == 0)
      ) {
        return sols(babs, cur2);
      }
    }
	return 0;
  };


  return babbling.reduce((acc, cur) => {
	return acc+sols(babs, cur);
  }, 0);

}

 

작성하고 보니 그렇게 썩 좋은 코드는 아닌것 같다. 재귀함수를 쓰지 않고 간결하게 해결할 방법이 있을것 같은데..

제출 결과 정상적으로 실행되는 것은 확인됐다.

 

3. 다른 풀이 리뷰

정규식으로 풀어낸 방법이 있던데,, 정규식이 뭔지는 다음에 알아보도록 하자.

 

가장 눈에 띄었던 코드이다.

  1. 첫번째 for문 : 같은 문자열이 두번 반복되면 possible을 그대로 반환
  2. 두번째 for문 : 문자열을 조건의 문자열들(babbables)을 기준으로 쪼갠 뒤 앞뒤 공백을 제거한다.
  3. 모든 문자열들로 쪼갠 후 문자열이 남아있다면 possible을 그대로 반환, 문자열이 남아있지 않다면 1을 더해서 반환
function solution(babbling) {
    const babblables = ["aya", "ye", "woo", "ma"];

    return babbling.reduce((possible, babbl, index) => {
        for (let i = 0; i < babblables.length; i += 1) {
            if (babbl.includes(babblables[i].repeat(2))) return possible;
        }

        for (let i = 0; i < babblables.length; i += 1) {
            babbl = babbl.split(babblables[i]).join(' ').trim();
        }

        if (babbl) return possible;

        return possible += 1;
    }, 0)
}

문제 풀이방법이 직관적이고 간결하게 표현된것 같다. 지나가다 슥 봐도 무슨 소린지 알거같은 코드.

그리고, split을 저렇게 쓰는 사람은 처음봤다..너무 천재적

특정 문자열을 기준으로 쪼개니 그 문자열은 사라진다는 성질을 잘 이용한 것 같다.

 

근데 한가지 의문이 드는게 있다면, 굳이 for문에 i를 인덱스로 쓸 필요가 있었을까..?싶었던것. for..of 문을 사용하면 i를 선언하지 않아도 충분히 구현할 수 있을 것 같은데.

function solution(babbling) {
    const babblables = ["aya", "ye", "woo", "ma"];

    return babbling.reduce((possible, babbl, index) => {
        for (idx of babbables) {
            if (babbl.includes(idx.repeat(2))) return possible;
        }

        for (idx of babbables) {
            babbl = babbl.split(idx).join(' ').trim();
        }

        if (babbl) return possible;

        return possible += 1;
    }, 0)
}

 

이렇게 하면 조금 더 보기 쉬울듯.