๐ ๋ฌธ์ ํ์ํ๊ธฐ
- ๋ ธ๋ ๊ฐ์ฌ์ ์ฌ์ฉ๋ ๋จ์ด๋ค ์ค์ ํน์ ํค์๋๊ฐ ๋ช ๊ฐ ํฌํจ๋์ด ์๋์ง ๊ตฌํ๊ธฐ
- ํค์๋๋ ์์ผ๋์นด๋ ๋ฌธ์ ์ค ํ๋์ธ '?'๊ฐ ํฌํจ๋ ํจํด ํํ์ ๋ฌธ์์ด
- '?'๋ ๊ธ์ ํ๋๋ฅผ ์๋ฏธ, ์ด๋ค ๋ฌธ์ ์๋ ๋งค์นญ๋จ
- "fro??" => "frodo"(O), "front"(O) / "frame"(X), "frozen"(X)
- ๊ฐ์ฌ์ ์ฌ์ฉ๋ ๋ชจ๋ ๋จ์ด๋ค์ด ๋ด๊ธด ๋ฐฐ์ด
words
์ ์ฐพ๊ณ ์ ํ๋ ํค์๋๊ฐ ๋ด๊ธด ๋ฐฐ์ดqueries
๊ฐ ์ฃผ์ด์ง ๋, ๊ฐ ํค์๋ ๋ณ๋ก ๋งค์น๋ ๋จ์ด๊ฐ ๋ช ๊ฐ์ธ์ง ์์๋๋ก ๋ฐฐ์ด์ ๋ด์ ๋ฐํํ๊ธฐ - ๊ฐ์ฌ ๋จ์ด ์ ํ ์ฌํญ
- 2 ≤
words
์ ๊ธธ์ด ≤ 100,000 - 1 ≤ ๊ฐ ๊ฐ์ฌ์ ๋จ์ด ๊ธธ์ด ≤ 10,000
- 2 ≤ ์ ์ฒด ๊ฐ์ฌ ๋จ์ด ๊ธธ์ด์ ํฉ ≤ 1,000,000
- ๊ฐ์ฌ์ ๋์ผ ๋จ์ด๊ฐ ์ฌ๋ฌ ๋ฒ ๋์ฌ ๊ฒฝ์ฐ ์ค๋ณต์ ์ ๊ฑฐํ๊ณ words์๋ ํ๋๋ก๋ง ์ ๊ณต
- ๊ฐ ๊ฐ์ฌ ๋จ์ด๋ ์ค์ง ์ํ๋ฒณ ์๋ฌธ์๋ก๋ง ๊ตฌ์ฑ
- 2 ≤
- ๊ฒ์ ํค์๋ ์ ํ ์ฌํญ
- 2 ≤
queries
์ ๊ธธ์ด ≤ 100,000 - 1 ≤ ๊ฐ ๊ฒ์ ํค์๋์ ๊ธธ์ด ≤ 100,000
- 2 ≤ ์ ์ฒด ๊ฒ์ ํค์๋ ๊ธธ์ด์ ํฉ ≤ 1,000,000
- ๊ฒ์ ํค์๋๋ ์ค๋ณต๋ ์ ์์ผ๋ฉฐ, ์ค์ง ์ํ๋ฒณ ์๋ฌธ์์ ์์ผ๋ ์นด๋ ๋ฌธ์ '?'๋ก๋ง ๊ตฌ์ฑ
- ๊ฒ์ ํค์๋๋ ์์ผ๋์นด๋ ๋ฌธ์์ธ '?'๊ฐ ํ๋ ์ด์ ํฌํจ, '?'๋ ๊ฐ ๊ฒ์ ํค์๋์ ์ ๋์ฌ ํน์ ์ ๋ฏธ์ฌ
- 2 ≤
queries
์ ํฌํจ๋ ๊ฐ ๊ฒ์ ํค์๋๋ฅผ ํ๋์ฉ ์ํํ๋ฉด์, ์์ผ๋ ์นด๋ ๋ฌธ์๊ฐ ์๋ ์์น๋ฅผ ์ ์ธํ ์์น์ ๋ฌธ์๋ค์ด ์ผ์นํ๋์ง ํ๋์ฉ ํ์ธํ๋ ค๋ฉด ์ต์
์ ๊ฒฝ์ฐ ์ด ์๊ฐ๋ณต์ก๋๊ฐ ์ฝ O(100,000)(queries
์ ์๋ ๊ฒ์ ํค์๋ ํ๋์ฉ ์ํ) * O(100,000)(words
์ ๋จ์ด๋ค ํ๋์ฉ ์ํ) * O(10,000)(์์ผ๋ ์นด๋๋ฅผ ์ ์ธํ ๋ถ๋ถ ์ผ์น ์ฌ๋ถ ์ฒดํฌ) ์ ๋๊ฐ ๋๋ค. ๋น์ฐํ ๋งค์ฐ ๋นํจ์จ์ ์ด๋ฉฐ ๋์ฑ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ ์ฐพ์์ผํ๋ค.
ํด๋น ๋ฌธ์ ์์ ์์ผ๋ ์นด๋๋ ๋ฐ๋์ ๊ฐ ๊ฒ์ ํค์๋์ ์ ๋์ฌ ํน์ ์ ๋ฏธ์ฌ๋ผ๊ณ ํ์ผ๋ฏ๋ก, ๋ฌธ์์ด์ ๊ณตํต ์ ๋์ฌ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ฉฐ, ์ ๋์ฌ๋ ์ ๋ฏธ์ฌ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ฒ์ํ ์ ์๋ Trie ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ฉด, ํ์ ์๊ฐ์ ๋จ์ถํ ์ ์๋ค.
Trie(ํธ๋ผ์ด) ์๋ฃ๊ตฌ์กฐ
๋ฌธ์์ด์ ํจ์จ์ ์ผ๋ก ์ ์ฅํ๊ณ ๊ฒ์ํ๊ธฐ ์ํ ํธ๋ฆฌ(Tree) ์๋ฃ๊ตฌ์กฐ
์ฃผ๋ก ๋ฌธ์์ด์ ํค๋ก ๊ฐ์ง๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ, ๋ฌธ์์ด์ ์ ๋์ฌ(prefix)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์ฑํ๊ธฐ ๋๋ฌธ์ Prefix Tree๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค.
๐ ์ฝ๋ ์ค๊ณํ๊ธฐ
class TrieNode
๊ตฌํ- ํธ๋ผ์ด ์๋ฃ๊ตฌ์กฐ์ ๊ฐ ๋ ธ๋๋ฅผ ํํํ ํด๋์ค
- ํ๋กํผํฐ
children
: ๊ฐ ๋ ธ๋์ ํ์ ๋ ธ๋๋ฅผ ๋์ ๋๋ฆฌ ํํ๋ก ์ ์ฅ. ๋ฌธ์(char) ํ๋์ ๊ทธ ๋ค์ ๋ฌธ์์ ํด๋นํ๋ TreeNode ๊ฐ์ฒด๋ฅผ ํค-๊ฐ์ผ๋ก ๊ฐ์งlength_count
: ํด๋น ๋ ธ๋๋ฅผ ์์์ผ๋ก ์ด์ด์ง๋ ๋ฌธ์์ด๋ค์ ๊ธธ์ด์ ๋ฐ๋ฅธ ๊ฐ์๋ฅผ ๋์ ๋๋ฆฌ ํํ๋ก ์ ์ฅ. ํด๋น ๋ ธ๋ ๋ค์๋ถํฐ ๋ฌธ์์ด ๋๊น์ง์ ๊ธธ์ด์ ํด๋นํ๋ ๋ฌธ์์ด ๊ฐ์๋ฅผ ํค-๊ฐ์ผ๋ก ๊ฐ์งisEnd
: ๋ฌธ์์ด์ ๋ง์ง๋ง ๋ฌธ์์ธ์ง ์ฌ๋ถ๋ฅผ boolean ํํ๋ก ์ ์ฅ.
class Trie
๊ตฌํ- ํ๋กํผํฐ
root
: ๋ฃจํธ ๋ ธ๋. ๋น์ด ์๋ ์ํlength_count
: ํธ๋ผ์ด ๊ฐ์ฒด์ ์ ์ฅ๋ ๋ฌธ์์ด๋ค์ ๊ธธ์ด์ ๋ฐ๋ฅธ ๊ฐ์๋ฅผ ๋์ ๋๋ฆฌ ํํ๋ก ์ ์ฅ. ๋ฌธ์์ด์ ๊ธธ์ด์ ํด๋นํ๋ ๋ฌธ์์ด ๊ฐ์๋ฅผ ํค-๊ฐ์ผ๋ก ์ ์ฅ
- ๋ฉ์๋
insert_length_count(self, length)
:length
๊ธธ์ด์ ๋ฌธ์์ด์ ๋ฐ๋ผ ํธ๋ผ์ด ๊ฐ์ฒด์ ํด๋น ๊ธธ์ด์ ๋ฌธ์์ด์ด ์ ์ฅ๋ ๊ฐ์๋ฅผ ์ ์ฅ ๋ฐ ๊ฐฑ์ .insert(self, word)
: ์๋ก์ด ๋ฌธ์์ดword
๋ฅผ ํธ๋ผ์ด ๊ฐ์ฒด์ ์ถ๊ฐ.count_by_length(self, word)
:word
์ ๊ธธ์ด์ ๋ฐ๋ผ ํธ๋ผ์ด ๊ฐ์ฒด์ ๋ช๊ฐ ์ ์ฅ๋์ด ์๋์ง ๋ฐํ.count_starts_with(self, prefix, length)
: ํธ๋ผ์ด ๊ฐ์ฒด์prefix
๋ก ์์ํ๋ฉด์ ๊ทธ ๋ค์ ๊ธธ์ด๊ฐlength
์ธ ๋ฌธ์์ด์ด ๋ช๊ฐ ์ ์ฅ๋์ด ์๋์ง ๋ฐํ
- ํ๋กํผํฐ
solution(words, queries)
๊ตฌํ- ์ผ๋ฐ์ ์ธ ๋ฌธ์์ด์ ์ ์ฅํ ํธ๋ผ์ด ์ธ์คํด์ค trie์, ์์๋ฅผ ๋ค์ง์ ๋ฌธ์์ด์ ์ ์ฅํ ํธ๋ผ์ด ์ธ์คํด์ค reverse_trie๋ฅผ ์์ฑํ๋ค.
- words์ ์ ์ฅ๋ ๋จ์ด(word)๋ฅผ ์ํํ๋ฉด์, ๊ฐ๊ฐ trie์ reverse_trie์ ์ถ๊ฐํด์ค๋ค.
- ํค์๋๋ณ๋ก ๋งค์น๋ ๋จ์ด ๊ฐ์๋ฅผ ์ ์ฅํ ๋ฆฌ์คํธ answer๋ฅผ ์์ฑํ๋ค.
- queries์ ์ ์ฅ๋ ๊ฒ์ ํค์๋(query)๋ฅผ ์ํํ๋ฉด์ ์๋์ ๊ฐ์ด ์กฐ๊ฑด์ ๋ฐ๋ผ answer์ ๊ฐ์ ์ถ๊ฐํ๋ค.
- ํค์๋์ ์ฒซ ๊ธ์์ ๋ ๊ธ์๊ฐ ๋ชจ๋ "?"์ธ ๊ฒฝ์ฐ, ๋ฌธ์ ์กฐ๊ฑด์ ์ํด ๋ชจ๋ ์์ผ๋์นด๋ ๋ฌธ์๋ก ๊ตฌ์ฑ๋ ๊ฒ์ ํค์๋์ด๋ฏ๋ก, ๋จ์ํ trie.count_by_length(len(query))๋ฅผ ๊ตฌํ๋ค.
- ํค์๋์ ๋ ๊ธ์๊ฐ "?"์ธ ๊ฒฝ์ฐ, ์์ผ๋์นด๋๊ฐ ์ ๋ฏธ์ฌ๋ก ์ฐ์ธ ๊ฒ์ํค์๋์ด๋ฏ๋ก, ์์ผ๋์นด๋๋ฅผ ์ ์ธํ ์ ๋์ฌ๋ฅผ ๊ตฌํด trie.count_starts_with(์ ๋์ฌ, ๋จ์ ๊ธธ์ด)๋ฅผ ๊ตฌํ๋ค.
- ํค์๋์ ์ฒซ ๊ธ์๊ฐ "?"์ธ ๊ฒฝ์ฐ, ์์ผ๋์นด๋๊ฐ ์ ๋์ฌ๋ก ์ฐ์ธ ๊ฒ์ํค์๋์ด๋ฏ๋ก, ์์ผ๋์นด๋๋ฅผ ์ ์ธํ ์ ๋ฏธ์ฌ๋ฅผ ๊ตฌํด reverse_trie.count_starts_with(์ ๋ฏธ์ฌ, ๋จ์ ๊ธธ์ด)๋ฅผ ๊ตฌํ๋ค.
- queries์ ๋ชจ๋ ์์์ ๋ํ ํ์์ด ์ข ๋ฃ๋๋ฉด answer๋ฅผ ๋ฐํํ๋ค.
๐ ์๋ ํ์ฐจ ์์ ์ฌํญ
1ํ์ฐจ
ํจ์จ์ฑ ํ ์คํธ์์ ์๊ฐ ์ด๊ณผ๋ ์๋๊ณ ์คํจ๊ฐ ๋ ์ ๊ทธ ์ด์ ๋ฅผ ๋ชฐ๋ผ์ ๋ต๋ตํ๋๋ฐ, ์ง๋ฌธ๊ธ์์ ํจ์จ์ฑ 3๋ฒ์ "?????"์ ๊ฐ์ด ๊ฒ์ ํค์๋๊ฐ ๋ชจ๋ ์์ผ๋์นด๋ ๋ฌธ์์ธ ๊ฒฝ์ฐ๋ฅผ ์ดํด๋ณด๋ ์์ ๋ผ๊ณ ์๋ ค์ค์ ๊ฒจ์ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค. ํ์ฐธ์ ํค๋งจ ๊ฒ ์น๊ณ ๋ ์์ฃผ์์ฃผ ์ฌ์ํ ์ค์์๋ค^_ใ
๋๋ ๊ฒ์ ํค์๋๊ฐ ๋ชจ๋ ์์ผ๋์นด๋ ๋ฌธ์์ธ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด์, ํธ๋ผ์ด ๊ฐ์ฒด์ length_count
๋ผ๋ ๋์
๋๋ฆฌ ํ๋กํผํฐ๋ฅผ ๋ง๋ค์ด์คฌ์๋ค. ๊ทธ๋ฆฌ๊ณ ์๋ก์ด ๋ฌธ์์ด์ ์ ์ฅํ ๋ ๊ทธ ๋ฌธ์์ด์ ๊ธธ์ด๋ฅผ ํค๋ก ๊ฐ์ง ๊ฐ์ 1์ ๋ํด์ฃผ๋ ์์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํด์ ๋์ค์ ํธ๋ผ์ด ์ธ์คํด์ค์ ์ ์ฅ๋ ๋ฌธ์์ด ์ค ํน์ ๊ธธ์ด์ ๋ฌธ์์ด์ด ๋ช๊ฐ์ธ์ง ์ฝ๊ฒ ๊ตฌํ ์ ์๋๋ก ํ๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ ๋ฌธ์์ด์ ํธ๋ผ์ด์ ์ ์ฅํ ๋, word
์ ๊ธธ์ด๊ฐ ์๋๋ผ word
๊ทธ ์์ฒด๋ฅผ ์ธ์๋ก ๋๊ฒจ์คฌ๊ณ ..
๊ทธ ๊ฒฐ๊ณผ length_count
๋์
๋๋ฆฌ์ ํค๊ฐ ๋ฌธ์์ด์ ๊ธธ์ด๊ฐ ์๋, word๋ก ์ ์ฅ๋์ด ์์๋ค๐ซ
๊ฒฐ๊ณผ์ ์ผ๋ก insert_length_count
๋ฉ์๋์ word
๊ฐ ์๋๋ผ len(word)
๊ฐ ์ ๋ฌ๋๋๋ก ๊ณ ์ณ์ฃผ์๋๋ ๋ฌด์ฌํ ํต๊ณผํ๋ค. ์ง๊ธ์์ ์๊ฐํด๋ณด๋ฉด ๊ตณ์ด insert_length_count
๋ฉ์๋๋ฅผ ๋ฐ๋ก ๋์ง ์๊ณ , insert
๋ฉ์๋ ์์์ Trie ๊ฐ์ฒด์ length_count
๋ฅผ ๊ฐฑ์ ํด์ค๋ ๋์ง ์์๊น..์ถ๊ธดํ๋ค.
๐ ์ ๋ต ์ฝ๋
class TrieNode:
def __init__(self):
self.children = {}
self.length_count = {}
self.isEnd = False
class Trie:
def __init__(self):
self.root = TrieNode()
self.length_count = {}
def insert_length_count(self, length):
length_count = self.length_count
self.length_count[length] = self.length_count.get(length, 0) + 1
def insert(self, word):
node = self.root
word_length = len(word)
for i in range(word_length):
char = word[i]
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
length = word_length-i-1
length_count = node.length_count
length_count[length] = length_count.get(length, 0) + 1
node.isEnd = True
def count_by_length(self, word):
return self.length_count.get(word, 0)
def count_starts_with(self, prefix, length):
node = self.root
for char in prefix:
if char not in node.children:
return 0
node = node.children[char]
return node.length_count.get(length, 0)
def solution(words, queries):
trie = Trie()
reverse_trie = Trie()
for word in words:
trie.insert(word)
reverse_trie.insert(word[::-1])
trie.insert_length_count(len(word))
answer = []
for query in queries:
word_len = len(query)
if query[0] == query[-1] == "?":
answer.append(trie.count_by_length(word_len))
elif query[-1] == "?":
query = query.rstrip("?")
answer.append(trie.count_starts_with(query, word_len-len(query)))
else:
query = query.lstrip("?")
answer.append(reverse_trie.count_starts_with(query[::-1], word_len-len(query)))
return answer