PGMS_가사 검색 (Java)
[level 4] 가사 검색 - 60060
성능 요약
메모리: 819 MB, 시간: 1025.94 ms
구분
코딩테스트 연습 > 2020 KAKAO BLIND RECRUITMENT
채점결과
정확성: 25.0
효율성: 75.0
합계: 100.0 / 100.0
제출 일자
2025년 04월 23일 00:14:06
문제 설명
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
친구들로부터 천재 프로그래머로 불리는 "프로도"는 음악을 하는 친구로부터 자신이 좋아하는 노래 가사에 사용된 단어들 중에 특정 키워드가 몇 개 포함되어 있는지 궁금하니 프로그램으로 개발해 달라는 제안을 받았습니다.
그 제안 사항 중, 키워드는 와일드카드 문자중 하나인 '?'가 포함된 패턴 형태의 문자열을 뜻합니다. 와일드카드 문자인 '?'는 글자 하나를 의미하며, 어떤 문자에도 매치된다고 가정합니다. 예를 들어 "fro??"
는 "frodo"
, "front"
, "frost"
등에 매치되지만 "frame"
, "frozen"
에는 매치되지 않습니다.
가사에 사용된 모든 단어들이 담긴 배열 words
와 찾고자 하는 키워드가 담긴 배열 queries
가 주어질 때, 각 키워드 별로 매치된 단어가 몇 개인지 순서대로 배열에 담아 반환하도록 solution
함수를 완성해 주세요.
가사 단어 제한사항
words
의 길이(가사 단어의 개수)는 2 이상 100,000 이하입니다.- 각 가사 단어의 길이는 1 이상 10,000 이하로 빈 문자열인 경우는 없습니다.
- 전체 가사 단어 길이의 합은 2 이상 1,000,000 이하입니다.
- 가사에 동일 단어가 여러 번 나올 경우 중복을 제거하고
words
에는 하나로만 제공됩니다. - 각 가사 단어는 오직 알파벳 소문자로만 구성되어 있으며, 특수문자나 숫자는 포함하지 않는 것으로 가정합니다.
검색 키워드 제한사항
queries
의 길이(검색 키워드 개수)는 2 이상 100,000 이하입니다.- 각 검색 키워드의 길이는 1 이상 10,000 이하로 빈 문자열인 경우는 없습니다.
- 전체 검색 키워드 길이의 합은 2 이상 1,000,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 입니다.
출처: 프로그래머스 코딩 테스트 연습, https://school.programmers.co.kr/learn/challenges
문제 풀이
트라이 문제라고 생각한다. 여러 길이 (최대 10만)와 여러 쿼리 (최대 10만개) 가 있다.
각 문자를 정방향, 역방향 트라이를 만들어 넣고, 각 문자마다 n글자일 때 몇개의 단어가 있는지 저장한다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import java.util.*;
class Solution {
class Trie{
Trie[] children;
Map<Integer, Integer> countMap; // 각 길이별 가능한 개수
public Trie(){
children = new Trie[26];
countMap = new HashMap<>();
}
public void insert(String s, int length){
Trie node = this;
node.countMap.put(length, node.countMap.getOrDefault(length, 0) + 1);
for(char c : s.toCharArray()){
if(node.children[c-'a'] == null) node.children[c-'a'] = new Trie();
node = node.children[c-'a'];
node.countMap.put(length, node.countMap.getOrDefault(length, 0) + 1);
}
}
public int query(String s, int length){
Trie node = this;
// ? 전까지 일치하는 노드로
for(char c : s.toCharArray()){
if(c == '?') break;
if(node.children[c-'a'] == null) return 0;
node = node.children[c-'a'];
}
return node.countMap.getOrDefault(length, 0);
}
}
public int[] solution(String[] words, String[] queries) {
int[] res = new int[queries.length];
Trie normalTrie = new Trie();
Trie reverseTrie = new Trie();
Map<Integer, Integer> lenCount = new HashMap<>();
for(String w : words){
lenCount.put(w.length(), lenCount.getOrDefault(w.length(), 0) + 1);
normalTrie.insert(w, w.length());
StringBuilder sb = new StringBuilder(w);
sb.reverse();
reverseTrie.insert(sb.toString(), sb.toString().length());
}
for(int i=0; i<queries.length; i++){
String q = queries[i];
// "????????" 인 경우
if(q.charAt(0) == '?' && q.charAt(q.length() - 1) == '?'){
res[i] = lenCount.getOrDefault(q.length(), 0);
continue;
}
// xxxx????? 인 경우
if(q.charAt(q.length() - 1) == '?'){
res[i] = normalTrie.query(q, q.length());
}
// ?????xxxx 인 경우
else{
StringBuilder sb = new StringBuilder(q);
sb.reverse();
res[i] = reverseTrie.query(sb.toString(), sb.toString().length());
}
}
return res;
}
}