[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
친구들로부터 천재 프로그래머로 불리는 "프로도"는 음악을 하는 친구로부터 자신이 좋아하는 노래 가사에 사용된 단어들 중에 특정 키워드가 몇 개 포함되어 있는지 궁금하니 프로그램으로 개발해 달라는 제안을 받았습니다.
그 제안 사항 중, 키워드는 와일드카드 문자중 하나인 '?'가 포함된 패턴 형태의 문자열을 뜻합니다. 와일드카드 문자인 '?'는 글자 하나를 의미하며, 어떤 문자에도 매치된다고 가정합니다. 예를 들어 "fro??"
는 "frodo"
, "front"
, "frost"
등에 매치되지만 "frame"
, "frozen"
에는 매치되지 않습니다.
가사에 사용된 모든 단어들이 담긴 배열 words
와 찾고자 하는 키워드가 담긴 배열 queries
가 주어질 때, 각 키워드 별로 매치된 단어가 몇 개인지 순서대로 배열에 담아 반환하도록 solution
함수를 완성해 주세요.
words
의 길이(가사 단어의 개수)는 2 이상 100,000 이하입니다.words
에는 하나로만 제공됩니다.queries
의 길이(검색 키워드 개수)는 2 이상 100,000 이하입니다.'?'
로만 구성되어 있으며, 특수문자나 숫자는 포함하지 않는 것으로 가정합니다.'?'
가 하나 이상 포함돼 있으며, '?'
는 각 검색 키워드의 접두사 아니면 접미사 중 하나로만 주어집니다."??odo"
, "fro??"
, "?????"
는 가능한 키워드입니다."frodo"
('?'
가 없음), "fr?do"
('?'
가 중간에 있음), "?ro??"
('?'
가 양쪽에 있음)는 불가능한 키워드입니다.words | queries | result |
---|---|---|
["frodo", "front", "frost", "frozen", "frame", "kakao"] | ["fro??", "????o", "fr???", "fro???", "pro?"] | [3, 2, 4, 1, 0] |
"fro??"
는 "frodo"
, "front"
, "frost"
에 매치되므로 3입니다."????o"
는 "frodo"
, "kakao"
에 매치되므로 2입니다."fr???"
는 "frodo"
, "front"
, "frost"
, "frame"
에 매치되므로 4입니다."fro???"
는 "frozen"
에 매치되므로 1입니다."pro?"
는 매치되는 가사 단어가 없으므로 0 입니다.해당 문제는 Trei 구조로 해결해야하는 문제이다.
// 트라이 노드를 나타내는 클래스
class Node {
constructor(value = '') {
this.value = value; // 현재 노드의 문자 값
this.count = 0; // 현재 노드를 거쳐가는 단어의 수
this.children = {}; // 자식 노드들을 저장하는 객체 (key: 문자, value: Node)
}
}
// 트라이를 나타내는 클래스
class Trie {
constructor() {
this.root = new Node(); // 트라이의 root 노드 초기화
}
insert(word) { // 단어 삽입 메서드
let node = this.root;
for (let i=0; i<word.length; i++) {
const char = word[i];
if (!node.children[char]) node.children[char] = new Node(node.value + char); // 해당 문자가 없으면 새로운 노드 생성 후 추가
node.count++; // 현재 노드를 거치는 단어 수 증가
node = node.children[char]; // 다음 문자로 이동
}
node.count++; // 마지막 문자에 도달했을 때도 단어 수 증가
}
search(query) { // 쿼리 검색 메서드
let node = this.root;
for (let i=0; i<query.length; i++) {
const char = query[i];
if (char === '?') return node.count; // 와일드카트 '?'에 도달하면 현재까지의 단어 수 반환
if (!node.children[char]) return 0; // 해당 문자가 없으면 매치되는 것 없으므로 0 반환
node = node.children[char]; // 다음 문자로 이동
}
return 0;
}
}
function solution(words, queries) {
const triesByLengthAscend = Array.from({length:10001}, ()=>new Trie()); /* 정방향 트라이 배열 초기화 */
const triesByLengthDescend = Array.from({length:10001}, ()=>new Trie()); /* 역방향 트라이 배열 초기화 */
words.forEach(word => {
triesByLengthAscend[word.length].insert(word);
triesByLengthDescend[word.length].insert([...word].reverse().join(''));
});
return queries.map(query => query[0] === '?' ?
triesByLengthDescend[query.length].search([...query].reverse().join('')) :
triesByLengthAscend[query.length].search(query));
}