자동완성 문서의 문제 1 풀이 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""[3차] 자동완성 (프로그래머스 17685) 풀이와 검증."""
import random
import time
def solution(words):
root = {}
for w in words: # 트라이에 넣으며 칸마다 지나는 단어 수를 센다
node = root
for ch in w:
if ch not in node:
node[ch] = [0, {}] # [지나는 단어 수, 자식들]
node[ch][0] += 1
node = node[ch][1]
total = 0
for w in words:
node = root
typed = len(w) # 끝까지 쳐야 하는 경우가 기본
for i, ch in enumerate(w):
cnt, kids = node[ch]
if cnt == 1: # 이 앞부분을 가진 단어가 나 하나뿐
typed = i + 1
break
node = kids
total += typed
return total
def by_lcp(words):
"""단어마다 다른 단어와의 가장 긴 공통 앞부분 + 1(단어 길이를 넘지 않게)."""
def lcp(a, b):
k = 0
while k < len(a) and k < len(b) and a[k] == b[k]:
k += 1
return k
total = 0
for i, w in enumerate(words):
m = max((lcp(w, x) for j, x in enumerate(words) if j != i), default=0)
total += min(len(w), m + 1)
return total
def by_sorted_neighbors(words):
"""정렬하면 공통 앞부분이 가장 긴 상대는 바로 앞이나 뒤 단어다."""
def lcp(a, b):
k = 0
while k < len(a) and k < len(b) and a[k] == b[k]:
k += 1
return k
ws = sorted(words)
total = 0
for i, w in enumerate(ws):
m = 0
if i > 0:
m = max(m, lcp(w, ws[i - 1]))
if i + 1 < len(ws):
m = max(m, lcp(w, ws[i + 1]))
total += min(len(w), m + 1)
return total
def typed_each(words):
root = {}
for w in words:
node = root
for ch in w:
if ch not in node:
node[ch] = [0, {}]
node[ch][0] += 1
node = node[ch][1]
out = []
for w in words:
node, typed = root, len(w)
for i, ch in enumerate(w):
cnt, kids = node[ch]
if cnt == 1:
typed = i + 1
break
node = kids
out.append(typed)
return out
if __name__ == "__main__":
assert solution(["go", "gone", "guild"]) == 7 == by_lcp(["go", "gone", "guild"])
assert solution(["abc", "def", "ghi", "jklm"]) == 4
assert solution(["word", "war", "warrior", "world"]) == 15
assert typed_each(["word", "war", "warrior", "world"]) == [4, 3, 4, 4]
rng = random.Random(17685)
for _ in range(2000):
ws = list({"".join(rng.choice("ab") for _ in range(rng.randint(1, 6))) for _ in range(rng.randint(2, 12))})
if len(ws) < 2:
continue
assert solution(ws) == by_lcp(ws) == by_sorted_neighbors(ws), ws
# 단어 10만 개, 길이 합 100만
ws = list({"".join(rng.choice("abcde") for _ in range(10)) for _ in range(100_000)})
t0 = time.perf_counter()
solution(ws)
t = time.perf_counter() - t0
assert t < 10, t
print(f"{len(ws)} words: {t:.2f}s")
print("ALL CHECKS PASSED")