코딩 테스트/프로그래머스

[프로그래머스][Level4] 자동완성 - JAVA

갓생사는 김초원의 개발 블로그 2021. 7. 24. 18:51

[문제]

  • 2018 KAKAO BLIND RECRUITMENT

https://programmers.co.kr/learn/courses/30/lessons/17685

 

코딩테스트 연습 - [3차] 자동완성

자동완성 포털 다음에서 검색어 자동완성 기능을 넣고 싶은 라이언은 한 번 입력된 문자열을 학습해서 다음 입력 때 활용하고 싶어 졌다. 예를 들어, go 가 한 번 입력되었다면, 다음 사용자는 g

programmers.co.kr

 

[풀이]

단어 검색 관련 문제는 Trie 트리 자료구조를 이용하여 풀어야 한다. 

(단어 검색, 자동 완성 등등 --> 자동으로 "Trie"가 떠올라야함)

 

<Trie자료구조 JAVA 코드>

Trie 구현코드를 처음부터 끝까지 작성하기는 쉽지 않다.

개인적으로 이런 문제를 풀 때는 팀노트나 블로그에 기록해두고 그때 그때 찾아서 응용하는 편이다.

더보기
package com.algorithm.tree;

import java.util.HashMap;
import java.util.Map;

public class TrieTree {

    static class TrieNode {
        private Map<Character, TrieNode> childNodes = new HashMap<>();
        private boolean isLastChar;
    }

    static class Trie {
        TrieNode rootNode;

        public Trie() {
            rootNode = new TrieNode();
        }

        void insert(String word) {
            TrieNode thisNode = this.rootNode;

            for (int i = 0; i < word.length(); i++) {
                thisNode = thisNode.childNodes.computeIfAbsent(word.charAt(i), c -> new TrieNode());
            }
            thisNode.isLastChar = true;
        }

        boolean contains(String word) {
            TrieNode thisNode = this.rootNode;

            for (int i = 0; i < word.length(); i++) {
                char character = word.charAt(i);

                TrieNode node = thisNode.childNodes.get(character);

                if (node == null) {
                    return false;
                }

                thisNode = node;
            }
            return thisNode.isLastChar;
        }

        void delete(String word) {
            delete(this.rootNode, word, 0);
        }

        void delete(TrieNode thisNode, String word, int index) {
            char character = word.charAt(index);

            if (!thisNode.childNodes.containsKey(character)) {
                throw new Error("There is no [" + word + "] in this Trie.");
            }

            TrieNode childNode = thisNode.childNodes.get(character);
            index++;

            if (index == word.length()) {
                if (!childNode.isLastChar) {
                    throw new Error("There is no [" + word + "] in this Trie.");
                }
                childNode.isLastChar = false;

                if (childNode.childNodes.isEmpty()) {
                    thisNode.childNodes.remove(character);
                }
            } else {
                delete(childNode, word, index);

                if (!childNode.isLastChar && childNode.childNodes.isEmpty()) {
                    thisNode.childNodes.remove(character);
                }
            }
        }
    }
}

 

<접근 방법>

루트 노드부터 시작하여 자식 노드로 내려가며 문자 하나하나씩을 순차적으로 탐색한다. 

자식노드로 내려가면서 카운팅한다.(문자 입력 개수 카운팅)

이 문자가 현재 검색하려는 단어에만 들어가는지, 이외에 다른 단어에도 들어가는지를 알아내면 된다. 

1. 현재 검색하려는 단어에만 포함되는 문자라면? -> 문자 입력 개수 리턴

2. 현재 검색하려는 단어 이외에 다른 단어들에도 포함된다. -> 자식노드로 더 내려감

 static class TrieNode {
        private Map<Character, TrieNode> childNodes = new HashMap<>();
        private int cnt; // 해당 문자가 몇개의 단어들에 포함되는지 카운트
}

 

 

 

[코드]

package com.algorithm.kakaoTest.three;

import java.util.HashMap;
import java.util.Map;

/**
 * 자동 완성
 * 소요시간: 57분 38초
 */
public class Test03 {
    public static void main(String[] args) {
        String[] words =// {"go", "gone", "guild"};
                //{"word", "war", "warrior", "world"};
                {"abc", "def", "ghi", "jklm"};
        int answer = solution(words);
        System.out.println(answer);
    }

    static class TrieNode {
        private Map<Character, TrieNode> childNodes = new HashMap<>();
        private int cnt;
    }

    static class Trie {
        TrieNode rootNode;

        public Trie() {
            rootNode = new TrieNode();
        }

        void insert(String word) {
            TrieNode thisNode = this.rootNode;

            for (int i = 0; i < word.length(); i++) {
                thisNode = thisNode.childNodes.computeIfAbsent(word.charAt(i), c -> new TrieNode());
                thisNode.cnt++;
            }
        }

        int getInputCounts(String word) {
            int count = 0;
            TrieNode thisNode = this.rootNode;

            for (int i = 0; i < word.length(); i++) {
                char character = word.charAt(i);
                TrieNode node = thisNode.childNodes.get(character);
                count++;

                if (node.cnt == 1) {
                    return count;
                }
                thisNode = node;
            }
            return count;
        }
    }

    public static int solution(String[] words) {
        int answer = 0;

        // Trie 트리 생성
        Trie trie = new Trie();
        for (String word : words) {
            trie.insert(word);
        }

        for (String word : words) {
            answer += trie.getInputCounts(word);
        }
        return answer;
    }
}

 

참고
https://the-dev.tistory.com/2
https://the-dev.tistory.com/3