|
| 1 | +import java.util.*; |
| 2 | + |
| 3 | +class Solution { |
| 4 | + |
| 5 | + public int lowerBound(ArrayList<String> arr, String target, int start, int end) { |
| 6 | + while (start < end) { |
| 7 | + int mid = (start + end) / 2; |
| 8 | + // arr[mid]가 target보다 사전순으로 같거나 뒤에 있다면 |
| 9 | + if (arr.get(mid).compareTo(target) >= 0) end = mid; |
| 10 | + else start = mid + 1; |
| 11 | + } |
| 12 | + return end; |
| 13 | + } |
| 14 | + |
| 15 | + public int upperBound(ArrayList<String> arr, String target, int start, int end) { |
| 16 | + while (start < end) { |
| 17 | + int mid = (start + end) / 2; |
| 18 | + // arr[mid]가 target보다 사전순으로 뒤에 있다면 |
| 19 | + if (arr.get(mid).compareTo(target) > 0) end = mid; |
| 20 | + else start = mid + 1; |
| 21 | + } |
| 22 | + return end; |
| 23 | + } |
| 24 | + |
| 25 | + // 값이 [left_value, right_value]인 데이터의 개수를 반환하는 함수 |
| 26 | + public int countByRange(ArrayList<String> arr, String leftValue, String rightValue) { |
| 27 | + // 유의: lowerBound와 upperBound는 end 변수의 값을 배열의 길이로 설정 |
| 28 | + int rightIndex = upperBound(arr, rightValue, 0, arr.size()); |
| 29 | + int leftIndex = lowerBound(arr, leftValue, 0, arr.size()); |
| 30 | + return rightIndex - leftIndex; |
| 31 | + } |
| 32 | + |
| 33 | + // 모든 단어들을 길이마다 나누어서 저장하기 위한 리스트 |
| 34 | + ArrayList<ArrayList<String>> arr = new ArrayList<ArrayList<String>>(); |
| 35 | + // 모든 단어들을 길이마다 나누어서 뒤집어 저장하기 위한 리스트 |
| 36 | + ArrayList<ArrayList<String>> reversedArr = new ArrayList<ArrayList<String>>(); |
| 37 | + |
| 38 | + public int[] solution(String[] words, String[] queries) { |
| 39 | + ArrayList<Integer> ans = new ArrayList<Integer>(); |
| 40 | + |
| 41 | + // 단어의 길이는 10,000까지 가능 |
| 42 | + for (int i = 0; i < 10001; i++) { |
| 43 | + arr.add(new ArrayList<String>()); |
| 44 | + reversedArr.add(new ArrayList<String>()); |
| 45 | + } |
| 46 | + |
| 47 | + // 모든 단어를 접미사 와일드카드 배열, 접두사 와일드카드 배열에 각각 삽입 |
| 48 | + for (int i = 0; i < words.length; i++) { |
| 49 | + String word = words[i]; |
| 50 | + arr.get(word.length()).add(word); // 단어를 삽입 |
| 51 | + word = (new StringBuffer(word)).reverse().toString(); |
| 52 | + reversedArr.get(word.length()).add(word); // 단어를 뒤집어서 삽입 |
| 53 | + } |
| 54 | + |
| 55 | + // 이진 탐색을 수행하기 위해 각 단어 리스트 정렬 수행 |
| 56 | + for (int i = 0; i < 10001; i++) { |
| 57 | + Collections.sort(arr.get(i)); |
| 58 | + Collections.sort(reversedArr.get(i)); |
| 59 | + } |
| 60 | + |
| 61 | + // 쿼리를 하나씩 확인하며 처리 |
| 62 | + for (int i = 0; i < queries.length; i++) { |
| 63 | + String q = queries[i]; |
| 64 | + int res = 0; |
| 65 | + if (q.charAt(0) != '?') { // 접미사에 와일드 카드가 붙은 경우 |
| 66 | + res = countByRange(arr.get(q.length()), q.replaceAll("\\?", "a"), q.replaceAll("\\?", "z")); |
| 67 | + } |
| 68 | + else { // 접두사에 와일드 카드가 붙은 경우 |
| 69 | + q = (new StringBuffer(q)).reverse().toString(); |
| 70 | + res = countByRange(reversedArr.get(q.length()), q.replaceAll("\\?", "a"), q.replaceAll("\\?", "z")); |
| 71 | + } |
| 72 | + // 검색된 단어의 개수를 저장 |
| 73 | + ans.add(res); |
| 74 | + } |
| 75 | + |
| 76 | + // 배열로 바꾸어 반환 |
| 77 | + int[] answer = new int[ans.size()]; |
| 78 | + for (int i = 0; i < ans.size(); i++) { |
| 79 | + answer[i] = ans.get(i); |
| 80 | + } |
| 81 | + return answer; |
| 82 | + } |
| 83 | +} |
0 commit comments