πŸ“– [λ°±μ€€μ•Œκ³ λ¦¬μ¦˜ 풀이] Q.5670 νœ΄λŒ€ν° 자판 문제 풀이 - java

πŸ“– 문제

https://www.acmicpc.net/problem/5670

νœ΄λŒ€ν°μ—μ„œ 길이가 P인 μ˜λ‹¨μ–΄λ₯Ό μž…λ ₯ν•˜λ €λ©΄ λ²„νŠΌμ„ P번 λˆŒλŸ¬μ•Ό ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ‹œμŠ€ν…œν”„λ‘œκ·Έλž˜λ° 연ꡬ싀에 κ·Όλ¬΄ν•˜λŠ” μŠΉν˜μ—°κ΅¬μ›μ€ 사전을 μ‚¬μš©ν•΄ 이 μž…λ ₯을 더 빨리 ν•  수 μžˆλŠ” 자판 λͺ¨λ“ˆμ„ κ°œλ°œν•˜μ˜€λ‹€. 이 λͺ¨λ“ˆμ€ 사전 λ‚΄μ—μ„œ κ°€λŠ₯ν•œ λ‹€μŒ κΈ€μžκ°€ ν•˜λ‚˜λΏμ΄λΌλ©΄ κ·Έ κΈ€μžλ₯Ό λ²„νŠΌ μž…λ ₯ 없이 μžλ™μœΌλ‘œ μž…λ ₯ν•΄ μ€€λ‹€! μžμ„Έν•œ μž‘λ™ 과정을 μ„€λͺ…ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

1. λͺ¨λ“ˆμ΄ λ‹¨μ–΄μ˜ 첫 번째 κΈ€μžλ₯Ό μΆ”λ‘ ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€. 즉, μ‚¬μ „μ˜ λͺ¨λ“  단어가 같은 μ•ŒνŒŒλ²³μœΌλ‘œ μ‹œμž‘ν•˜λ”λΌλ„ λ°˜λ“œμ‹œ 첫 κΈ€μžλŠ” μ‚¬μš©μžκ°€ λ²„νŠΌμ„ 눌러 μž…λ ₯ν•΄μ•Ό ν•œλ‹€.

2. 길이가 1 이상인 λ¬Έμžμ—΄ c1c2...cn이 μ§€κΈˆκΉŒμ§€ μž…λ ₯λ˜μ—ˆμ„ λ•Œ, 사전 μ•ˆμ˜ λͺ¨λ“  c1c2...cn으둜 μ‹œμž‘ν•˜λŠ” 단어가 c1c2...cncλ‘œλ„ μ‹œμž‘ν•˜λŠ” κΈ€μž cκ°€ μ‘΄μž¬ν•œλ‹€λ©΄ λͺ¨λ“ˆμ€ μ‚¬μš©μžμ˜ λ²„νŠΌ μž…λ ₯ 없이도 μžλ™μœΌλ‘œ cλ₯Ό μž…λ ₯ν•΄ μ€€λ‹€. 그렇지 μ•Šλ‹€λ©΄ μ‚¬μš©μžμ˜ μž…λ ₯을 κΈ°λ‹€λ¦°λ‹€.

예λ₯Ό λ“€λ©΄, 사전에 β€œhello”, β€œhell”, β€œheaven”, β€œgoodbye” 4개의 단어가 있고 μ‚¬μš©μžκ°€ β€œh”λ₯Ό μž…λ ₯ν•˜λ©΄ λͺ¨λ“ˆμ€ μžλ™μœΌλ‘œ β€œe”λ₯Ό μž…λ ₯ν•΄ μ€€λ‹€. μ‚¬μ „μƒμ˜ β€œhβ€λ‘œ μ‹œμž‘ν•˜λŠ” 단어듀은 λͺ¨λ‘ κ·Έ 뒀에 β€œe”가 였기 λ•Œλ¬Έμ΄λ‹€. κ·ΈλŸ¬λ‚˜ 단어듀 쀑 β€œhelβ€λ‘œ μ‹œμž‘ν•˜λŠ” 것도, β€œheaβ€λ‘œ μ‹œμž‘ν•˜λŠ” 것도 있기 λ•Œλ¬Έμ— μ—¬κΈ°μ„œ λͺ¨λ“ˆμ€ μ‚¬μš©μžμ˜ μž…λ ₯을 κΈ°λ‹€λ¦°λ‹€. μ΄μ–΄μ„œ μ‚¬μš©μžκ°€ β€œl”을 μž…λ ₯ν•˜λ©΄ λͺ¨λ“ˆμ€ β€œl”을 μžλ™μœΌλ‘œ μž…λ ₯ν•΄ μ€€λ‹€. κ·ΈλŸ¬λ‚˜ μ—¬κΈ°μ„œ λλ‚˜λŠ” 단어인 β€œhell”과 그렇지 μ•Šμ€ 단어인 β€œhello”가 κ³΅μ‘΄ν•˜λ―€λ‘œ λͺ¨λ“ˆμ€ λ‹€μ‹œ μž…λ ₯을 κΈ°λ‹€λ¦°λ‹€. μ‚¬μš©μžκ°€ β€œhell”을 μž…λ ₯ν•˜κΈ° μ›ν•œλ‹€λ©΄ μ—¬κΈ°μ„œ μž…λ ₯을 멈좜 것이고, β€œhello”λ₯Ό μž…λ ₯ν•˜κΈ° μ›ν•œλ‹€λ©΄ 직접 β€œo” λ²„νŠΌμ„ λˆŒλŸ¬μ„œ μž…λ ₯ν•΄ μ€˜μ•Ό ν•œλ‹€. λ”°λΌμ„œ β€œhello”λ₯Ό μž…λ ₯ν•˜λ €λ©΄ μ‚¬μš©μžλŠ” 총 3번 λ²„νŠΌμ„ λˆŒλŸ¬μ•Ό ν•˜κ³ , β€œhell”, β€œheaven”은 2λ²ˆμ΄λ‹€. β€œheavenβ€μ˜ 경우 β€œheβ€μ—μ„œ β€œa”λ₯Ό μž…λ ₯ν•˜λ©΄ λ°”λ‘œ 뒷뢀뢄이 λͺ¨λ‘ μžλ™μœΌλ‘œ μž…λ ₯되기 λ•Œλ¬Έμ΄λ‹€. λΉ„μŠ·ν•˜κ²Œ, β€œgoodbyeβ€λŠ” 단 ν•œ 번만 λ²„νŠΌμ„ λˆŒλŸ¬λ„ μž…λ ₯이 μ™„λ£Œλ  것이닀. β€œg”λ₯Ό μž…λ ₯ν•˜λŠ” μˆœκ°„ 뒀에 μ˜€λŠ” κΈ€μžκ°€ 항상 μœ μΌν•˜λ―€λ‘œ λκΉŒμ§€ μžλ™μœΌλ‘œ μž…λ ₯되기 λ•Œλ¬Έμ΄λ‹€. μ΄λ•Œ 사전에 μžˆλŠ” 단어듀을 μž…λ ₯ν•˜κΈ° μœ„ν•΄ λ²„νŠΌμ„ λˆŒλŸ¬μ•Ό ν•˜λŠ” 횟수의 평균은 (3 + 2 + 2 + 1)/4 = 2.00이닀.

사전이 μ£Όμ–΄μ‘Œμ„ λ•Œ, 이 λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜λ©΄μ„œ 이와 같이 각 단어λ₯Ό μž…λ ₯ν•˜κΈ° μœ„ν•΄ λ²„νŠΌμ„ λˆŒλŸ¬μ•Ό ν•˜λŠ” 횟수의 평균을 κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ‹œμ˜€.

πŸ” 접근법

Q.5670 νœ΄λŒ€ν° 자판 λ¬Έμ œμž…λ‹ˆλ‹€.

μ—¬λŸ¬κ°€μ§€ μ˜μ–΄ 단어듀이 μ €μž₯ λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 그리고 μ›ν•˜λŠ” 단어λ₯Ό μžλ™μ™„μ„±μ„ μ΄μš©ν•΄ 이와 같이 각 단어λ₯Ό μž…λ ₯ν•˜κΈ° μœ„ν•΄ λ²„νŠΌμ„ λˆŒλŸ¬μ•Ό ν•˜λŠ” 횟수의 평균을 κ΅¬ν•˜λŠ” λ¬Έμ œμž…λ‹ˆλ‹€.

λ‹¨μ–΄μ˜ μžλ™μ™„μ„±, λŒ€ν‘œμ μΈ Trie μ•Œκ³ λ¦¬μ¦˜ 문제 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€.

Trieλ₯Ό μ΄μš©ν•΄ 풀이가 κ°€λŠ₯ν•©λ‹ˆλ‹€.

Trieλ₯Ό λ§Œλ“€μ–΄ Trie에 각 단어듀을 λͺ¨λ‘ μ €μž₯ν•΄μ€λ‹ˆλ‹€.

κ·Έ ν›„ μ›ν•˜λŠ” 단어λ₯Ό Trieλ₯Ό μ΄μš©ν•΄ νƒμƒ‰ν•©λ‹ˆλ‹€.

μ΄λ•Œ μžλ™μ™„μ„± κΈ°λŠ₯을 μ΄μš©ν•˜κΈ° μœ„ν•΄ 주어진 쑰건이 μžˆμŠ΅λ‹ˆλ‹€.

λ°”λ‘œ λ‹€μŒ 경우의 μˆ˜κ°€ ν•œκ°€μ§€μ΄μ–΄μ•Όλ§Œ λ°”λ‘œ μžλ™μ™„μ„±μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

λ‹€μŒμ— 올 수 μžˆλŠ” μ•ŒνŒŒλ²³ 경우의 μˆ˜κ°€ 두가지 이상이라면 λ°˜λ“œμ‹œ μ‚¬μš©μžμ˜ μž…λ ₯이 ν•„μš”ν•©λ‹ˆλ‹€.

그리고 λ˜ν•œ 이미 μ–΄λ– ν•œ 단어가 완성이 λ˜μ—ˆμ„ λ•Œ κ·Έ λ’€λ‘œ μƒˆλ‘œμš΄ μ•ŒνŒŒλ²³μ΄ 이어져 λ‹€μŒ 단어λ₯Ό νƒμƒ‰ν•˜κΈ° μœ„ν•΄μ„œλ„

μ‚¬μš©μžμ˜ μž…λ ₯이 ν•„μš”ν•©λ‹ˆλ‹€.

Trie 탐색에 이λ₯Ό μ΄μš©ν•˜λ©΄ 닡을 ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.

                //μžμ‹λ…Έλ“œ 크기가 2이상이면 선택지가 2가지 μ΄μƒμ΄λ―€λ‘œ μž…λ ₯ν•΄μ•Όν•œλ‹€
                if(thisNode.childNodes.size() >= 2){
                    count++;
                }

                //μžμ‹ λ…Έλ“œκ°€ ν•˜λ‚˜λΌλ©΄ μžλ™μ™„μ„±μ΄ λ˜μ§€λ§Œ λ§Œμ•½ 이미 λ¬Έμžμ—΄μ΄ μ™„μ„± 된 μƒνƒœλΌλ©΄ λ‹€λ₯Έ λ¬Έμžμ—΄μ„ μœ„ν•΄ μž…λ ₯ν•΄μ•Όν•œλ‹€.
                if(thisNode.childNodes.size() == 1 && thisNode.isLastString){
                    count++;
                }

πŸ’» μ½”λ“œ

package problem.string.trie;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main_5670 {

    static int n;
    static Trie trie;
    static String[] word;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input;
        while ( (input = br.readLine()) != null ){
            n = Integer.parseInt(input);
            word = new String[n];
            for(int i=0; i<n; i++){
                word[i] = br.readLine();
            }
            inputTrie();
            solve();
        }
    }

    private static void inputTrie() {
        Arrays.sort(word);
        trie = new Trie();
        for(int i=0; i<n; i++){
            trie.insert(word[i]);
        }
    }

    private static void solve(){
        int count = 0;
        Arrays.sort(word);
        for(int i=0; i<n; i++) {
            int result = trie.contains(word[i]);
            count+=result;
        }
        System.out.println(String.format("%.2f",(double)count/n));
    }

    static class TrieNode {

        public Map<Character, TrieNode> childNodes = new HashMap<>();
        private boolean isLastString;

        public Map<Character, TrieNode> getChildNodes() {
            return this.childNodes;
        }

        public boolean isLastString() {
            return this.isLastString;
        }

        public void setLastString(boolean lastString) {
            this.isLastString = lastString;
        }
    }

    static class Trie {
        private TrieNode rootNode;

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

        // μžμ‹ λ…Έλ“œ μΆ”κ°€
        boolean insert(String str) {
            TrieNode thisNode = this.rootNode;

            for (int i = 0; i < str.length(); i++) {
                char currentString = str.charAt(i);
                if(thisNode.childNodes.get(currentString) == null){
                    thisNode.childNodes.put(currentString,new TrieNode());
                }
                thisNode = thisNode.childNodes.get(currentString);
            }
            thisNode.setLastString(true);
            return true;
        }

        int contains(String str) {
            TrieNode thisNode = this. rootNode;
            int count = 1;
            thisNode = thisNode.childNodes.get(str.charAt(0));
            for (int i = 1; i < str.length(); i++) {
                char currentChar = str.charAt(i);

                //μžμ‹λ…Έλ“œ 크기가 2이상이면 선택지가 2가지 μ΄μƒμ΄λ―€λ‘œ μž…λ ₯ν•΄μ•Όν•œλ‹€
                if(thisNode.childNodes.size() >= 2){
                    count++;
                }

                //μžμ‹ λ…Έλ“œκ°€ ν•˜λ‚˜λΌλ©΄ μžλ™μ™„μ„±μ΄ λ˜μ§€λ§Œ λ§Œμ•½ 이미 λ¬Έμžμ—΄μ΄ μ™„μ„± 된 μƒνƒœλΌλ©΄ λ‹€λ₯Έ λ¬Έμžμ—΄μ„ μœ„ν•΄ μž…λ ₯ν•΄μ•Όν•œλ‹€.
                if(thisNode.childNodes.size() == 1 && thisNode.isLastString){
                    count++;
                }

                thisNode = thisNode.childNodes.get(currentChar);
            }
            return count;
        }
    }
}
Written on May 5, 2021