[Programmers/ level3] 보석 μ‡Όν•‘πŸ™„

3 λΆ„ μ†Œμš”


πŸ‘©λ¬Έμ œ

[λ³Έ λ¬Έμ œλŠ” μ •ν™•μ„±κ³Ό νš¨μœ¨μ„± ν…ŒμŠ€νŠΈ 각각 μ μˆ˜κ°€ μžˆλŠ” λ¬Έμ œμž…λ‹ˆλ‹€.]

개발자 μΆœμ‹ μœΌλ‘œ 세계 졜고의 κ°‘λΆ€κ°€ 된 μ–΄ν”ΌμΉ˜λŠ” 슀트레슀λ₯Ό 받을 λ•Œλ©΄ 이λ₯Ό ν’€κΈ° μœ„ν•΄ μ˜€ν”„λΌμΈ 맀μž₯에 쇼핑을 ν•˜λŸ¬ κ°€κ³€ ν•©λ‹ˆλ‹€. μ–΄ν”ΌμΉ˜λŠ” 쇼핑을 ν•  λ•Œλ©΄ 맀μž₯ μ§„μ—΄λŒ€μ˜ νŠΉμ • λ²”μœ„μ˜ 물건듀을 λͺ¨λ‘ 싹쓸이 κ΅¬λ§€ν•˜λŠ” μŠ΅κ΄€μ΄ μžˆμŠ΅λ‹ˆλ‹€. μ–΄λŠ λ‚  슀트레슀λ₯Ό ν’€κΈ° μœ„ν•΄ 보석 맀μž₯에 쇼핑을 ν•˜λŸ¬ κ°„ μ–΄ν”ΌμΉ˜λŠ” μ΄μ „μ²˜λŸΌ μ§„μ—΄λŒ€μ˜ νŠΉμ • λ²”μœ„μ˜ 보석을 λͺ¨λ‘ κ΅¬λ§€ν•˜λ˜ νŠΉλ³„νžˆ μ•„λž˜ λͺ©μ μ„ λ‹¬μ„±ν•˜κ³  μ‹Άμ—ˆμŠ΅λ‹ˆλ‹€.

μ§„μ—΄λœ λͺ¨λ“  μ’…λ₯˜μ˜ 보석을 적어도 1개 이상 ν¬ν•¨ν•˜λŠ” κ°€μž₯ 짧은 ꡬ간을 μ°Ύμ•„μ„œ ꡬ맀

예λ₯Ό λ“€μ–΄ μ•„λž˜ μ§„μ—΄λŒ€λŠ” 4μ’…λ₯˜μ˜ 보석(RUBY, DIA, EMERALD, SAPPHIRE) 8κ°œκ°€ μ§„μ—΄λœ μ˜ˆμ‹œμž…λ‹ˆλ‹€.

μ§„μ—΄λŒ€ 번호 1 2 3 4 5 6 7 8
보석 이름 DIA RUBY RUBY DIA DIA EMERALD SAPPHIRE DIA

μ§„μ—΄λŒ€μ˜ 3λ²ˆλΆ€ν„° 7λ²ˆκΉŒμ§€ 5개의 보석을 κ΅¬λ§€ν•˜λ©΄ λͺ¨λ“  μ’…λ₯˜μ˜ 보석을 적어도 ν•˜λ‚˜ 이상씩 ν¬ν•¨ν•˜κ²Œ λ©λ‹ˆλ‹€.

μ§„μ—΄λŒ€μ˜ 3, 4, 6, 7번의 λ³΄μ„λ§Œ κ΅¬λ§€ν•˜λŠ” 것은 쀑간에 νŠΉμ • ꡬ간(5번)이 λΉ μ§€κ²Œ λ˜λ―€λ‘œ μ–΄ν”ΌμΉ˜μ˜ μ‡Όν•‘ μŠ΅κ΄€μ— λ§žμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ§„μ—΄λŒ€ 번호 μˆœμ„œλŒ€λ‘œ λ³΄μ„λ“€μ˜ 이름이 μ €μž₯된 λ°°μ—΄ gemsκ°€ λ§€κ°œλ³€μˆ˜λ‘œ μ£Όμ–΄μ§‘λ‹ˆλ‹€. μ΄λ•Œ λͺ¨λ“  보석을 ν•˜λ‚˜ 이상 ν¬ν•¨ν•˜λŠ” κ°€μž₯ 짧은 ꡬ간을 μ°Ύμ•„μ„œ return ν•˜λ„λ‘ solution ν•¨μˆ˜λ₯Ό μ™„μ„±ν•΄μ£Όμ„Έμš”. κ°€μž₯ 짧은 κ΅¬κ°„μ˜ μ‹œμž‘ μ§„μ—΄λŒ€ λ²ˆν˜Έμ™€ 끝 μ§„μ—΄λŒ€ 번호λ₯Ό μ°¨λ‘€λŒ€λ‘œ 배열에 λ‹΄μ•„μ„œ return ν•˜λ„λ‘ ν•˜λ©°, λ§Œμ•½ κ°€μž₯ 짧은 ꡬ간이 μ—¬λŸ¬ 개라면 μ‹œμž‘ μ§„μ—΄λŒ€ λ²ˆν˜Έκ°€ κ°€μž₯ μž‘μ€ ꡬ간을 return ν•©λ‹ˆλ‹€.

πŸ‘©μ œν•œμ‚¬ν•­

  • gems λ°°μ—΄μ˜ ν¬κΈ°λŠ” 1 이상 100,000 μ΄ν•˜μž…λ‹ˆλ‹€.
    • gems λ°°μ—΄μ˜ 각 μ›μ†ŒλŠ” μ§„μ—΄λŒ€μ— λ‚˜μ—΄λœ 보석을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
    • gems λ°°μ—΄μ—λŠ” 1번 μ§„μ—΄λŒ€λΆ€ν„° μ§„μ—΄λŒ€ 번호 μˆœμ„œλŒ€λ‘œ 보석이름이 μ°¨λ‘€λŒ€λ‘œ μ €μž₯λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
    • gems λ°°μ—΄μ˜ 각 μ›μ†ŒλŠ” 길이가 1 이상 10 μ΄ν•˜μΈ μ•ŒνŒŒλ²³ λŒ€λ¬Έμžλ‘œλ§Œ κ΅¬μ„±λœ λ¬Έμžμ—΄μž…λ‹ˆλ‹€.


πŸ‘©μž…μΆœλ ₯ 예

gems result
[β€œDIA”, β€œRUBY”, β€œRUBY”, β€œDIA”, β€œDIA”, β€œEMERALD”, β€œSAPPHIRE”, β€œDIA”] [3, 7]
[β€œAA”, β€œAB”, β€œAC”, β€œAA”, β€œAC”] [1, 3]
[β€œXYZ”, β€œXYZ”, β€œXYZ”] [1, 1]
[β€œZZZ”, β€œYYY”, β€œNNNN”, β€œYYY”, β€œBBB”] [1, 5]


πŸ™‹β€β™€οΈμ ‘κ·Ό 방법

  • λ‚˜μ˜ μ ‘κ·Ό 방법(μ˜€λ‹΅)
    • 처음 λ‚˜λŠ” 각 λ³΄μ„λ§ˆλ‹€ μ§„μ—΄λœ μ‹œμž‘ μœ„μΉ˜μ™€ λμœ„μΉ˜λ₯Ό ꡬ해 무쑰건 ν¬ν•¨λ˜λŠ” ꡬ간을 κ΅¬ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ ‘κ·Όν–ˆλ‹€.

      μ§„μ—΄λŒ€ 번호 1 2 3 4 5 6 7 8
      보석 이름 DIA RUBY RUBY DIA DIA EMERALD SAPPHIRE DIA
    • 예λ₯Ό λ“€μ–΄ μœ„ μ˜ˆμ‹œμ—μ„œ EMERALD와 SAPPHIREλŠ” 각각 1κ°œλ°–μ— μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ ν•΄λ‹Ή μœ„μΉ˜λŠ” 무쑰건 ν¬ν•¨λ˜μ–΄μ•Ό ν•œλ‹€.

    • μ΄λ ‡κ²Œ μ ‘κ·Όν–ˆμ„ λ•Œ λ¬Έμ œμ μ€ gems 배열을 λͺ¨λ‘ λŒμ•„ 각 λ³΄μ„μ˜ μœ„μΉ˜λ₯Ό νŒŒμ•…ν•΄μ•Ό ν–ˆμ„ 뿐 μ•„λ‹ˆλΌ 각 λ³΄μ„μ˜ μ‹œμž‘, 끝 μœ„μΉ˜λ₯Ό 담은 λ°°μ—΄(str, end)μ—μ„œ μ΅œλŒ€κ°’, μ΅œμ†Œκ°’μ„ 비ꡐ해 κ΅¬ν•œ ꡬ간이 μ›λž˜ gems λ°°μ—΄μ˜ 크기보닀 크게 쀄어듀지 μ•Šμ•˜λ‹€. κ²°κ΅­ gems λ°°μ—΄ 전체λ₯Ό μ™„μ „ νƒμƒ‰ν•˜λŠ” 결과와 λΉ„μŠ·ν•œ O(N^2)의 λ³΅μž‘λ„λ₯Ό κ°€μ§€κ²Œ λœλ‹€.


  • μ ‘κ·Ό 방법(ν•΄λ‹΅) : πŸ””νˆ¬ ν¬μΈν„°πŸ””
    1. μ‹œμž‘ν•˜λŠ” λ²”μœ„(left)와 끝 λ²”μœ„(right)λ₯Ό κ°€λ¦¬ν‚€λŠ” 두 개의 포인터λ₯Ό 두고, λͺ¨λ‘ μ§„μ—΄λŒ€ μ²˜μŒμ„ κ°€λ¦¬ν‚€κ²Œ ν•œλ‹€.
    2. ꡬ간 μ•ˆμ˜ λ³΄μ„μ˜ μ’…λ₯˜λ₯Ό νŒŒμ•…ν•œλ‹€.
      • μ΄λ•Œ λ°°μ—΄ μŠ¬λΌμ΄μ‹±μ„ μ΄μš©ν•  경우, μŠ¬λΌμ΄μ‹± λ²”μœ„λ§ŒνΌμ˜ μ‹œκ°„λ³΅μž‘λ„λ₯Ό 가지기 λ•Œλ¬Έμ— μ’…λ₯˜λ₯Ό νŒŒμ•…ν•˜κΈ° μœ„ν•΄ λ³΄μ„μ˜ μ’…λ₯˜λ₯Ό key둜 κ°–λŠ” λ”•μ…”λ„ˆλ¦¬λ₯Ό μ‚¬μš©ν•œλ‹€.
      • 2-1. λ²”μœ„ μ•ˆμ— μžˆλŠ” λ³΄μ„μ˜ μ’…λ₯˜κ°€ 전체 λ³΄μ„μ˜ μ’…λ₯˜λ³΄λ‹€ 같을 경우, left 값을 1μ”© μ¦κ°€ν•˜λ©° ν•΄λ‹Ή rightμ—μ„œμ˜ μ΅œμ†Œ ꡬ간(best)을 ꡬ해 후보값(result)에 μΆ”κ°€μ‹œν‚¨λ‹€.
      • 2-2. λ²”μœ„ μ•ˆμ— μžˆλŠ” λ³΄μ„μ˜ μ’…λ₯˜κ°€ 전체 λ³΄μ„μ˜ μ’…λ₯˜λ³΄λ‹€ 적을 경우, rightλ₯Ό 1 μ¦κ°€μ‹œν‚¨λ‹€.
      • 2-3. left, right 값이 배열을 λ„˜κΈ° μ „κΉŒμ§€ 2번 과정을 λ°˜λ³΅ν•œλ‹€.
    3. 후보값 쀑 κ΅¬κ°„μ˜ 크기가 κ°€μž₯ 짧은 값이 정닡이 λœλ‹€. (κ΅¬κ°„μ˜ 크기가 동일할 경우, μ‹œμž‘μ΄ λΉ λ₯Έ ꡬ간이 정닡이 λœλ‹€.)


πŸ™‹β€β™€οΈμ½”λ“œ

from collections import defaultdict

def solution(gems):
    num = len(set(gems))
    result = []
    dic = defaultdict(int)

    left = 0
    best = 0 # μ΅œμ†Œ ꡬ간인지λ₯Ό λ‚˜νƒ€λ‚΄λŠ” κ°’, 0 λ˜λŠ” 1
    for right in range(len(gems)):
        dic[gems[right]] += 1 # right+1둜 μΆ”κ°€λ˜λŠ” 보석 포함
        right += 1
        while len(dic) == num: # κ΅¬κ°„μ˜ 보석 μ’…λ₯˜κ°€ 전체 보석 μ’…λ₯˜μ™€ 동일할 λ•Œ
            dic[gems[left]] -= 1 # left+1을 μœ„ν•΄ ν•΄λ‹Ή μœ„μΉ˜ 보석 μ œμ™Έ
            if dic[gems[left]] == 0: # μ—†λŠ” λ³΄μ„μ˜ 경우 μ’…λ₯˜μ—μ„œ 제거
                del dic[gems[left]]
            left += 1 # left+1 진행
            best = 1 # μ΅œμ†Œ κ΅¬κ°„μž„μ„ ν‘œμ‹œ
        if best:
            result.append([left, right]) # rightμ—μ„œμ˜ μ΅œμ†Œ ꡬ간을 후보에 μΆ”κ°€
            best = 0

    return sorted(result, key = lambda x: (x[1]-x[0], x[0]))[0]


πŸ‘©β€πŸ’»λ°°μš΄ 점

  • collections.defaultdict(): λ”•μ…”λ„ˆλ¦¬ κΈ°λ³Έκ°’/μ΄ˆκΈ°κ°’ μ„€μ •
    • defaultdict(int): 초기 κ°’ 0
    • defaultdict(list): 초기 κ°’ []
    • defaultdict(set): 초기 κ°’ ()
  • λ°°μ—΄μ˜ κ΅¬κ°„λ§ˆλ‹€ νŠΉμ • 계산을 진행해야 ν•  λ•Œ 투 포인터λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 νš¨κ³Όμ μ΄λ‹€.


πŸ™‡β€β™€οΈν’€μ΄μ— λΆ€μ‘±ν•œ 뢀뢄이 μžˆλ‹€λ©΄ λ§μ”€ν•΄μ£Όμ„Έμš”! κ°μ‚¬ν•©λ‹ˆλ‹€!


πŸ“ƒ μ°Έκ³ 

λŒ“κΈ€λ‚¨κΈ°κΈ°