TIL/알고리즘 공부

프로그래머스 Lv. 2 Swift 알고리즘 - 메뉴 리뉴얼

여의도사노비 2023. 1. 28. 21:05
728x90

못 풀었다...

이건 그냥 못 풀었다 ㅠㅠ

다른 분들의 글을 참고해보니 DFS, Combination 등등 비슷한 맥락이지만 각자만의 방식대로 풀이가 있었다.

그중 내가 가장 인상깊게 본 코드는 아래 블로그를 운영하신 분의 코드다... 다른 코드들이 굉장히 긴 코드들이 많았는데 이 분의 코드는 엄청나게 정갈하다..! 그리고 뭔가 클로져를 팍팍 쓰신 것이... 클로져 달인이신 느낌을 받았다.

이 문제는 표시해놓고 꼭 다시 한 번 복습해야겠다!

(참고한 블로그: https://aios.tistory.com/entry/swift-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%A9%94%EB%89%B4-%EB%A6%AC%EB%89%B4%EC%96%BC)

 

 

 

이 문제를 풀면서 생각한 논리적 흐름

  1. 처음 문제를 풀때 각 코스 가짓수에 포함되는 유저의 메뉴들 중 가장 많은 수가 나온 Alphabet들을 활용해주면 되겠구나..! 라는 생각으로 시작했다.
  2. 하지만 막상 코드를 짜면서 생각해보니 원소의 조합이 중요했다. 단지 많이 등장한 알파벳이 아니라 이 알파벳과 저 알파벳이 서로 같은 원소 자리에 있어야 인정받을 수 있다는 것...!
  3. 그래서 결국 course의 메뉴 개수(ex. 2, 3, 4)를 기준으로 조합의 케이스를 전부 구한 뒤
  4. 그 조합 케이스가 몇번 동일하게 반복되는지 확인하고 가장 많이 반복된 케이스를 배열에 담아주면 된다.
  5. 논리는 간단한데 문제 풀이는 간단하지 않다 ㅠ 이게 정말 세심하게 챙겨야할 조건들이 많아서 꼼꼼하게 봐야할 듯!

 

 

* 짜다만 코드...

import Foundation

func solution(_ orders:[String], _ course:[Int]) -> [String] {
    let alphabet = (97...122).map({Character(UnicodeScalar($0)).uppercased()})
    var arr = Array(repeating: [Character](), count: orders.count)
    var dict: [Character: Int] = [:]

    // 기본세팅: 각 유저들이 선택한 메뉴 저장하기
    for i in 0..<orders.count {
        for j in orders[i] {
            arr[i].append(j)
        }
    }

    for menu in course {
        // 기본세팅: 메뉴가 각각 몇번 나왔는지 확인
        for j in alphabet {
            dict[Character(j)] = 0
        }

        for user in arr {
            if user.count >= menu {
                for selectedMenu in user {
                    dict[selectedMenu]! += 1
                }
            }
        }
        print("\(menu)코스: \(dict.sorted {$0.0 < $1.0})")
    }
    return []
}

 

 

* 참고한 코드

func combination(_ str: String, len: Int) -> [String] {
    if str.count < len {
        return []
    }

    if len == 1 {
        return str.map { String($0) }
    }

    guard let first = str.first else { return []}
    let newString = String(str.dropFirst())
    let c = String(first)
    
    return combination(newString, len: len)
        + combination(newString, len: len - 1).map { c + $0 }
}

func solution(_ orders:[String], _ course:[Int]) -> [String] {

    let ret = course.map { num -> [String] in
        let counts = orders
            .map { combination($0, len: num) }
            .flatMap { $0 }
            .map { $0.sorted().map{ String($0) }.joined() }
            .reduce(into: [:], { counts, word in
                counts[word, default: 0] += 1
            })
        guard let max = counts.values.max(), max >= 2
        else { return [] }
        let keys = counts.filter { $0.value == max }
            .map { $0.key }
        return keys
    }
    return ret.joined().sorted()
}
 

 

정리(Today I Learned)

1. 재귀를 사용할 때 나오는 return이 너무 헷갈린다. 단지 마지막에 특정 값을 반환하는게 아니라 특정 메서드를 그대로 다시 return 해주다 보니 결국 마지막에 어떤 값들이, 그리고 그 중간 중간 발생한 브랜치의 메서드 리턴값들이 어떻게 나올지 잘 감이 안온다. Combination 관련된 알고리즘 문제는 조금 더 복습을 많이 해야할 것 같다.