[Swift - ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€] μˆœμœ„ 검색

2021. 5. 29. 20:11γ†πŸ§‘πŸ»‍πŸ’» iOS 개발/Swift - Algorithm - Solutions

πŸ•΅πŸ»  λ¬Έμ œ 해석


카카였의 ν•˜λ°˜κΈ° κ²½λ ₯ 개발자 κ³΅κ°œμ±„μš©μ—μ„œ μ§€μ›μžλ“€μ˜ μ§€μ›μ„œμ™€ μ½”λ”©ν…ŒμŠ€νŠΈ 점수λ₯Ό μ΄μš©ν•˜μ—¬ μ±„μš©μ— μ°Έμ—¬ν•œ κ°œλ°œνŒ€λ“€μ˜ 문의 사항에 λ§žλŠ” μ§€μ›μžλ“€μ˜ 수λ₯Ό μ œκ³΅ν•˜λ €κ³  ν•©λ‹ˆλ‹€. μ§€μ›μ„œ ν•­λͺ©μ€ 총 4가지, κ°œλ°œμ–Έμ–΄, 지원 직ꡰ, 지원 κ²½λ ₯, μ„ ν˜Έν•˜λŠ” μ†ŒμšΈν‘Έλ“œλ‘œ κ΅¬μ„±λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€. 

λ”°λΌμ„œ μ§€μ›μ„œμ˜ 4가지 ν•­λͺ©, μ½”λ”©ν…ŒμŠ€νŠΈ 점수 1가지, 총 5κ°€μ§€μ˜ ν•­λͺ©μ„ μžλ£Œκ΅¬μ‘°μ— μ €μž₯ν•˜κ³  문의 사항듀을 νƒμƒ‰ν•˜λ©° 각각 기쀀을 μΆ©μ‘±ν•˜λŠ” μ§€μ›μžλ“€μ˜ 수λ₯Ό κ΅¬ν•˜λ©΄ 될 것 κ°™μŠ΅λ‹ˆλ‹€. 

κ·Έλ ‡κ²Œ ν•˜κΈ°μœ„ν•΄ KeyλŠ” μ§€μ›μžκ°€ μΆ©μ‘±ν•˜λŠ” 쑰건을 μ˜λ―Έν•˜λŠ” String, ValueλŠ” ν•΄λ‹Ή 쑰건을 λ§Œμ‘±ν•˜λŠ” μ§€μ›μžλ“€μ˜ 점수 λ°°μ—΄ [Int] ν˜•νƒœμ˜ λ”•μ…”λ„ˆλ¦¬μ— μ§€μ›μžλ“€μ˜ 정보λ₯Ό μ €μž₯ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 var dic: [String : [Int]] = [:]
  • info : "java backend junior pizza 150"
  • query : "java and backend and junior and pizza 150"

λ”•μ…”λ„ˆλ¦¬μ— μ €μž₯ν•˜κΈ° 전에, λ¨Όμ € μž…λ ₯을 μ‚΄νŽ΄λ³΄λ©΄ μ£Όμ–΄μ§€λŠ” μ§€μ›μžλ“€μ˜ μ •λ³΄λŠ” 곡백 (" ")으둜 κ΅¬λΆ„λ˜μ–΄μžˆκ³  λ°˜λ©΄μ— κ°œλ°œνŒ€λ“€μ˜ λ¬Έμ˜μ‚¬ν•­μ€ 곡백과 (" "), ("and")둜 κ΅¬λΆ„λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€. λ¨Όμ € 이λ₯Ό λ™μΌν•œ ν˜•νƒœλ‘œ λ§Œλ“€μ–΄ μ£Όμ–΄μ•Ό 비ꡐλ₯Ό ν•  수 있게 되겠죠?

for applicant in info {
	// applicant -> "java backend junior pizza 150"
       
        var components = applicant.components(separatedBy: " ")
        
        // components -> ["java", "backend", "junior", "pizza", "150"]
        let score = Int(components[4])!
}

for q in query {
    	// q -> "java and backend and junior and pizza and 150"
    
        var components = q.components(separatedBy: " ").filter{$0 != "and"}
        
        // components -> ["java", "backend", "junior", "pizza", "150"]
        let score = Int(components[4])!
 }

자주 μ‚¬μš©ν•˜λŠ” components λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ 곡백을 κΈ°μ€€μœΌλ‘œ λ‚˜λˆˆ [String] λ°°μ—΄λ‘œ μ§€μ›μžμ˜ 정보λ₯Ό λ‚˜λˆŒ 수 μžˆμŠ΅λ‹ˆλ‹€. κ·Έ ν›„ μ½”λ”©ν…ŒμŠ€νŠΈμ˜ μ μˆ˜λŠ” Int μƒμ„±μžλ₯Ό μ΄μš©ν•˜μ—¬ λ”°λ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.  λ§ˆμ°¬κ°€μ§€λ‘œ query도 μ²˜λ¦¬ν•΄μ€€ ν›„, filterλ₯Ό μ΄μš©ν•˜μ—¬ "and"λ₯Ό κ±ΈλŸ¬λƒ…λ‹ˆλ‹€.

μ—¬κΈ°μ„œ λ¬Έμ˜μ‚¬ν•­ μ•ˆμ˜ "-" ν•­λͺ© λ•Œλ¬Έμ— κ³¨μΉ˜κ°€ μ•„νŒŒμ§‘λ‹ˆλ‹€. 

λ§Œμ•½ μ§€μ›μžμ˜ 정보가 "java backend junior pizza 150" 둜 주어지면 점수λ₯Ό μ œμ™Έν•˜κ³  μ§€μ›μžκ°€ μΆ©μ‘±ν•˜λŠ” 쑰건이 총 16가지가 λ©λ‹ˆλ‹€. 

["java", "backend", "junior", "pizza"]
["java", "backend", "junior", "-"]
["java", "backend", "-", "pizza"]
["java", "backend", "-", "-"]
["java", "-", "junior", "pizza"]
["java", "-", "junior", "-"]
["java", "-", "-", "pizza"]
["java", "-", "-", "-"]
["-", "backend", "junior", "pizza"]
["-", "backend", "junior", "-"]
["-", "backend", "-", "pizza"]
["-", "backend", "-", "-"]
["-", "-", "junior", "pizza"]
["-", "-", "junior", "-"]
["-", "-", "-", "pizza"]
["-", "-", "-", "-"]

λ”°λΌμ„œ μž¬κ·€ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ μ§€μ›μžκ°€ μΆ©μ‘±ν•˜λŠ” μ‘°κ±΄λ“€μ˜ λͺ¨λ“  μΌ€μ΄μŠ€λ“€μ„ ꡬ해주어 λ”•μ…”λ„ˆλ¦¬μ— 점수λ₯Ό μ €μž₯ν•΄ μ£Όκ² μŠ΅λ‹ˆλ‹€. 

 // arr: μ§€μ›μžλ“€μ˜ 정보λ₯Ό 담은 λ°°μ—΄
 // curIdx: "-"둜 λ°”κΏ” μ €μž₯ν•  ν•­λͺ©μ˜ 인덱슀
 // score: μ§€μ›μžμ˜ 점수
 
 func combination(_ arr: [String], _ curIdx: Int, _ score: Int) {
        if curIdx == arr.count {
            let result = arr.joined()
            
            // ν•­λͺ©λ“€μ„ ν•˜λ‚˜μ˜ λ¬Έμžμ—΄λ‘œ ν•©μ³μ„œ λ”•μ…”λ„ˆλ¦¬μ— 점수λ₯Ό μ €μž₯
            if dic[result] != nil {
                dic[result]!.append(score)
            } else {
                dic[result] = [score]
            }
        } else {
            var temp = arr
            
            combination(temp, curIdx + 1, score)
            temp[curIdx] = "-"
            combination(temp, curIdx + 1, score)
        }
        return
   }

μ €μž₯이 λλ‚˜λ©΄ 이 ν›„ 이뢄 탐색을 μœ„ν•΄ λ”•μ…”λ„ˆλ¦¬μ˜ [Int] 배열을 μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•΄μ€λ‹ˆλ‹€. 이제 쿼리듀을 νƒμƒ‰ν•˜λ©° λ”•μ…”λ„ˆλ¦¬μ— 점수 배열이 μ‘΄μž¬ν•˜λ©΄ 쿼리의 μ μˆ˜μ™€ λΉ„κ΅ν•˜μ—¬ κ·Έ 개수λ₯Ό answer배열에 μΆ”κ°€ν•˜κ³  μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ ν•΄λ‹Ή 쑰건을 λ§Œμ‘±ν•˜λŠ” μ§€μ›μžκ°€ μ—†μœΌλ―€λ‘œ 0을 μΆ”κ°€ν•©λ‹ˆλ‹€. 

μ΄λ•Œ λ¬Έμ œμ—μ„œλŠ” μ§€μ›μžκ°€ μ΅œλŒ€ 5만 λͺ…이고 점수의 μ΅œλŒ“κ°’μ€ 10만 점, 쿼리 λ°°μ—΄μ˜ μ΅œλŒ€ 크기가 10만이라고 ν•©λ‹ˆλ‹€. μ§€μ›μžλ“€μ˜ μ μˆ˜κ°€ λͺ¨λ‘ λ‹€λ₯΄λ‹€κ³  κ°€μ •ν•  수 μžˆμœΌλ―€λ‘œ μ„ ν˜• νƒμƒ‰μœΌλ‘œλŠ” μ‹œκ°„ μ΄ˆκ³Όκ°€ λ‚  수 μžˆμŠ΅λ‹ˆλ‹€. 이뢄 탐색을 μ΄μš©ν•˜μ—¬, LowerBoundλ₯Ό ꡬ해 μš”κ΅¬ 점수 μ΄μƒμ˜ μ μˆ˜λ“€μ„ μΉ΄μš΄νŠΈν•΄μ€λ‹ˆλ‹€.

πŸ“¦  λ¬Έμ œ 풀이 & μ½”λ“œ


import Foundation

func solution(_ info:[String], _ query:[String]) -> [Int] {
    var dic: [String : [Int]] = [:]
    var answer = [Int]()
    
    func combination(_ arr: [String], _ curIdx: Int, _ score: Int) {
        if curIdx == arr.count {
            let result = arr.joined()
            
            if dic[result] != nil {
                dic[result]!.append(score)
            } else {
                dic[result] = [score]
            }
        } else {
            var temp = arr
            combination(temp, curIdx + 1, score)
            temp[curIdx] = "-"
            combination(temp, curIdx + 1, score)
        }
        return
    }
    
    func lowerbound(_ arr: [Int], _ score: Int) -> Int {
        var low = 0
        var high = arr.count
        
        while low < high {
            let mid = (low + high) / 2
            if score <= arr[mid] {
                high = mid
            } else {
                low = mid + 1
            }
        }
        // low = score μ΄μƒμ˜ μ μˆ˜κ°€ 처음 λ“±μž₯ν•˜λŠ” 인덱슀
        return arr.count - low
    }
    
    for applicant in info {
        var components = applicant.components(separatedBy: " ")
        let score = Int(components[4])!
        components.removeLast()
        combination(components, 0, score)
    }
    
    // 이뢄탐색을 μœ„ν•΄ 미리 μ μˆ˜λ°°μ—΄μ„ μ •λ ¬
    for (query, scores) in dic {
        let sortedScores = scores.sorted(by: <)
        dic[query] = sortedScores
    }

    for q in query {
        var components = q.components(separatedBy: " ").filter{$0 != "and"}
        let score = Int(components[4])!
        components.removeLast()
        let key = components.joined()
        
        // ν•΄λ‹Ή 쑰건을 μΆ©μ‘±ν•˜λŠ” μ μˆ˜κ°€ μ—†λ‹€λ©΄ 0
        if dic[key] == nil {
            answer.append(0)
        } else {
        // ν•΄λ‹Ή 쑰건을 μΆ©μ‘±ν•˜λŠ” 점수 배열이 μ‘΄μž¬ν•˜λ©΄, 쿼리의 μš”κ΅¬ 점수둜 이뢄 탐색
            answer.append(lowerbound(dic[key]!, score))
        }
    }
    return answer
}

 

🀷🏻‍♂️ 더 생각해보기


νž˜λ“€κ²Œ 톡과.. 😿

처음 λ¬Έμ œμ— μ ‘κ·Όν•  λ•ŒλŠ” λ‹¨μˆœνžˆ 쿼리와 μ§€μ›μžλ“€μ˜ 정보λ₯Ό 닀쀑 for문을 톡해 λΉ„κ΅ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λŠ” λͺ¨λ‘ ν†΅κ³Όν–ˆμœΌλ‚˜ νš¨μœ¨μ„± ν…ŒμŠ€νŠΈμ—μ„œ μ‹œκ°„ μ΄ˆκ³Όκ°€ λ‚˜λ”κ΅°μš”..

문제의 쑰건을 μ°Ύμ•„λ³΄λ‹ˆ μ§€μ›μžλ“€μ˜ μ΅œλŒ€ μˆ˜κ°€ 5만, 쿼리 λ°°μ—΄μ˜ μ΅œλŒ€ 크기가 10λ§Œμ΄λ‹ˆ λ‹¨μˆœ 곱만 해봐도 50얡이 λ„˜μ–΄κ°‘λ‹ˆλ‹€. μ΄λ²ˆμ—λŠ” 혼자 ν•΄κ²°ν•˜μ§€ λͺ»ν•˜κ³  λ‹€λ₯Έ λΆ„λ“€μ˜ 풀이λ₯Ό μ°Έκ³ ν•˜μ—¬ 문제λ₯Ό λ‹€μ‹œ ν’€μ–΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

νš¨μœ¨μ„± ν…ŒμŠ€νŠΈμ—μ„œλ„ μ‹œκ°„μ΄ κ½€ κ±Έλ¦¬λŠ” κ²ƒμœΌλ‘œ 보아 μž…λ ₯으둜 λ“€μ–΄μ˜¨ μ§€μ›μž 정보와 쿼리λ₯Ό λ‚˜λˆ„μ–΄ μ €μž₯ν•  λ•Œ filter {} λŒ€μ‹ μ— andκ°€ λ“±μž₯ν•˜λŠ” μΈλ±μŠ€λŠ” μ •ν•΄μ Έ μžˆμœΌλ―€λ‘œ νŠΉμ • μΈλ±μŠ€λ§Œμ„ λͺ¨μ•„ "\(components [0])\(components [2])..."이런 μ‹μœΌλ‘œ μ²˜λ¦¬ν•˜λ©΄ 더 λΉ λ₯Έ μ†λ„λ‘œ 톡과할 수 μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€.

πŸ‘πŸ» μ°Έκ³