ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [2020 카카였 λΈ”λΌμΈλ“œ μ½”λ”©ν…ŒμŠ€νŠΈ] 가사 검색 JAVA
    STUDY πŸ’›/μ•Œκ³ λ¦¬μ¦˜ 2021. 2. 17. 16:49
    λ°˜μ‘ν˜•

     

    [문제]


    programmers.co.kr/learn/courses/30/lessons/60060

     

    μ½”λ”©ν…ŒμŠ€νŠΈ μ—°μŠ΅ - 가사 검색

     

    programmers.co.kr

    μΉœκ΅¬λ“€λ‘œλΆ€ν„° 천재 ν”„λ‘œκ·Έλž˜λ¨Έλ‘œ λΆˆλ¦¬λŠ” ν”„λ‘œλ„λŠ” μŒμ•…μ„ ν•˜λŠ” μΉœκ΅¬λ‘œλΆ€ν„° μžμ‹ μ΄ μ’‹μ•„ν•˜λŠ” λ…Έλž˜ 가사에 μ‚¬μš©λœ 단어듀 쀑에 νŠΉμ • ν‚€μ›Œλ“œκ°€ λͺ‡ 개 ν¬ν•¨λ˜μ–΄ μžˆλŠ”μ§€ κΆκΈˆν•˜λ‹ˆ ν”„λ‘œκ·Έλž¨μœΌλ‘œ κ°œλ°œν•΄ λ‹¬λΌλŠ” μ œμ•ˆμ„ λ°›μ•˜μŠ΅λ‹ˆλ‹€.
    κ·Έ μ œμ•ˆ 사항 쀑, ν‚€μ›Œλ“œλŠ” μ™€μΌλ“œμΉ΄λ“œ λ¬Έμžμ€‘ ν•˜λ‚˜μΈ '?'κ°€ ν¬ν•¨λœ νŒ¨ν„΄ ν˜•νƒœμ˜ λ¬Έμžμ—΄μ„ λœ»ν•©λ‹ˆλ‹€. μ™€μΌλ“œμΉ΄λ“œ 문자인 '?'λŠ” κΈ€μž ν•˜λ‚˜λ₯Ό μ˜λ―Έν•˜λ©°, μ–΄λ–€ λ¬Έμžμ—λ„ λ§€μΉ˜λœλ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€.
    예λ₯Ό λ“€μ–΄ "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??"('?'κ°€ μ–‘μͺ½μ— 있음)λŠ” λΆˆκ°€λŠ₯ν•œ ν‚€μ›Œλ“œμž…λ‹ˆλ‹€.

    [μž…μΆœλ ₯ 예]

    - "fro??"λŠ” "frodo", "front", "frost"에 λ§€μΉ˜λ˜λ―€λ‘œ 3μž…λ‹ˆλ‹€.
    - "????o"λŠ” "frodo", "kakao"에 λ§€μΉ˜λ˜λ―€λ‘œ 2μž…λ‹ˆλ‹€.
    - "fr???"λŠ” "frodo", "front", "frost", "frame"에 λ§€μΉ˜λ˜λ―€λ‘œ 4μž…λ‹ˆλ‹€.
    - "fro???"λŠ” "frozen"에 λ§€μΉ˜λ˜λ―€λ‘œ 1μž…λ‹ˆλ‹€.
    - "pro?"λŠ” λ§€μΉ˜λ˜λŠ” 가사 단어가 μ—†μœΌλ―€λ‘œ 0 μž…λ‹ˆλ‹€.

     


     

    [Solution 1]
    - 이진탐색 μ‚¬μš© X
     - μ •ν™•μ„± ν…ŒμŠ€νŠΈ 18/18 톡과 (25.0)
     - νš¨μœ¨μ„± ν…ŒμŠ€νŠΈ 2/5 톡과 (30.0)
     - 합계 55.0 / 100.0

    package programmers_가사검색;
    
    public class Main {
    	public static void main(String[] args) {
    		//TEST CASE
    		String[] words = {"frodo", "front", "frost", "frozen", "frame", "kakao"};
    		String[] queries = {"fro??", "????o", "fr???", "fro???", "pro?"};
    		
    		int[] sol = Solution(words, queries);
    		
    		for(int i=0; i<sol.length; i++) {
    			System.out.print(sol[i]+" ");
    		}
    	}
    
    	static int[] Solution(String[] words, String[] queries) {
    		int[] answer = new int [queries.length];
    		
    		for(int i=0; i<queries.length; i++) {
    			char tmp = queries[i].charAt(0);		//쿼리의 첫번째 단어
    			
    			String compareStr = "";
    			boolean location = false;		//μ™€μΌλ“œμΉ΄λ“œμ˜ μœ„μΉ˜ -> true:접두사에	false:접미사에 μœ„μΉ˜
    			int qcnt = 0;					//μ™€μΌλ“œμΉ΄λ“œ COUNT
    			
    			//μ™€μΌλ“œμΉ΄λ“œκ°€ 접두사에 μœ„μΉ˜
    			if(tmp == '?') {
    				location = true;
    				qcnt = 1;
    			} else {
    				//μ™€μΌλ“œμΉ΄λ“œκ°€ 접미사에 μœ„μΉ˜ν•˜λ©΄ 첫번째 μ•ŒνŒŒλ²³ μ—°κ²°
    				compareStr = Character.toString(tmp);
    			}
    			
    			//λ‘λ²ˆμ§Έ μ•ŒνŒŒλ²³λΆ€ν„° 탐색
    			for(int j=1; j<queries[i].length(); j++) {
    				tmp = queries[i].charAt(j);
    				
    				if(tmp == '?') {
    					qcnt++;				//μ™€μΌλ“œμΉ΄λ“œλ©΄ μ™€μΌλ“œμΉ΄λ“œ CNT++
    				} else {
    					compareStr += Character.toString(tmp);		//μ•ŒνŒŒλ²³μ΄λΌλ©΄ 뒀에 단어듀과 비ꡐ할 compareStr에 μ—°κ²°ν•΄μ€Œ
    				}
    			}
    			
    			//단어듀과 비ꡐ μ‹œμž‘
    			int ansCnt = 0;
    			for(int j=0; j<words.length; j++) {
    				if(words[j].length() != compareStr.length()+qcnt) continue;				//단어와 길이가 λ‹€λ₯΄λ©΄ 비ꡐ할 ν•„μš”X
    				
    				if(location) {
    					//μ™€μΌλ“œμΉ΄λ“œκ°€ 접두사에 μœ„μΉ˜ν•˜λ©΄ μ™€μΌλ“œμΉ΄λ“œCNT(qcnt) 이후뢀터 μ•ŒνŒŒλ²³κ³Ό 비ꡐ
    					if(words[j].substring(qcnt, words[j].length()).equals(compareStr)) ansCnt++;
    				} else {
    					//μ™€μΌλ“œμΉ΄λ“œκ°€ 접미사에 μœ„μΉ˜ν•˜λ©΄ μ²«λ²ˆμ§ΈλΆ€ν„° qcntκΉŒμ§€μ˜ μ•ŒνŒŒλ²³κ³Ό 비ꡐ
    					if(words[j].substring(0, compareStr.length()).equals(compareStr)) ansCnt++;
    				}
    			}
    			answer[i] = ansCnt;
    		}
    		
            return answer;
        }
    }

     

     

    [Solution 2]
    - 이진탐색 μ‚¬μš©
    - Lower Bound, Upper Bound κ΅¬ν˜„

    package programmers_가사검색;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class Main {
    	public static void main(String[] args) {
    		String[] words = {"frodo", "front", "frost", "frozen", "frame", "kakao"};
    		String[] queries = {"fro??", "????o", "fr???", "fro???", "pro?"};
    		
    		int[] sol = Solution(words, queries);
    		
    		for(int i=0; i<sol.length; i++) {
    			System.out.print(sol[i]+" ");
    		}
    	}
    
    	static int[] Solution(String[] words, String[] queries) {
    		ArrayList<ArrayList<String>> arr = new ArrayList<ArrayList<String>>();
    		ArrayList<ArrayList<String>> revArr = new ArrayList<ArrayList<String>>();
    		
    		int[] answer = new int [queries.length];
    		
    		//λ‹¨μ–΄μ˜ κΈΈμ΄λ³„λ‘œ Array Init
    		for(int i = 0; i < 10001; i++) { 
    			arr.add(new ArrayList<String>()); 
    			revArr.add(new ArrayList<String>());
    		}
    		
    		//단어듀 μ—­λ°©ν–₯으둜 μ €μž₯
    		for(int i=0; i<words.length; i++) {
    			String word = words[i];
    			arr.get(word.length()).add(word);
    			revArr.get(word.length()).add((new StringBuffer(word)).reverse().toString());
    		}
    		
    		//단어 μ •λ ¬
    		for(int i=0; i<10001; i++) {
    			Collections.sort(arr.get(i));
    			Collections.sort(revArr.get(i));
    		}
    
    		//쿼리문의 μ™€μΌλ“œμΉ΄λ“œ μœ„μΉ˜ 확인
    		for(int i=0; i<queries.length; i++) {
    			int ansCnt = 0;
    			//μ™€μΌλ“œμΉ΄λ“œκ°€ 접두에 μœ„μΉ˜ -> 쿼리도 λ’€μ§‘μ–΄μ„œ revArr와 비ꡐ
    			if(queries[i].charAt(0) == '?') {
    				String tmp = (new StringBuffer(queries[i])).reverse().toString();		//쿼리 단어 뒀집
    				
    				//쿼리의 ?듀이 ν‘œν˜„ν•  수 μžˆλŠ” κ°€μž₯ μž‘μ€κ°’κ³Ό κ°€μž₯ ν°κ°’μœΌλ‘œ λ§Œλ“€μ–΄μ„œ(replace) μ°ΎκΈ°
    				int rightIndex = upper_bound(revArr.get(queries[i].length()), tmp.replaceAll("\\?", "z"));	//μΈμžκ°’(ex: frozzz)보닀 큰 첫번째 idx
    				int leftIndex = lower_bound(revArr.get(queries[i].length()), tmp.replaceAll("\\?", "a"));	//μΈμžκ°’(ex: froaaa)κ³Ό κ°™κ±°λ‚˜ 큰 첫번째 idx
    				
    				ansCnt = rightIndex - leftIndex;		//초과 μœ„μΉ˜ - μ‹œμž‘ μœ„μΉ˜
    			} else {		//μ™€μΌλ“œμΉ΄λ“œκ°€ 접미에 μœ„μΉ˜ -> arr와 비ꡐ
    				int rightIndex = upper_bound(arr.get(queries[i].length()), queries[i].replaceAll("\\?", "z"));
    				int leftIndex = lower_bound(arr.get(queries[i].length()), queries[i].replaceAll("\\?", "a"));
    				
    				ansCnt = rightIndex - leftIndex;
    			}
    			answer[i] = ansCnt;
    		}
    		
            return answer;
        }
    	
    	//lower_bound : 주어진 μΈμžκ°’(target)보닀 ν¬κ±°λ‚˜ 같은 값이 처음 λ‚˜μ˜¬ λ•Œμ˜ μœ„μΉ˜ return
    	static int lower_bound(ArrayList<String> list, String target) {
    		int begin = 0;
    		int end = list.size();
    		
    		while(begin < end) {
    			int mid = (begin+end)/2;
    			//list[mid]κ°€ target보닀 μ‚¬μ „μˆœμœΌλ‘œ κ°™κ±°λ‚˜ 크면(뒀에 있으면)
    			if(list.get(mid).compareTo(target) >= 0) end = mid;
    			else begin = mid + 1;
    		}
    		
    		return begin;
    	}
    
    	//upper_bound : μΈμžκ°’(target)보닀 큰 첫번째 μœ„μΉ˜λ₯Ό λ°˜ν™˜
    	static int upper_bound(ArrayList<String> list, String target) {
    		int begin = 0;
    	    int end = list.size();
    	    
    	    while(begin < end) {
    	    	int mid = (begin+end)/2;
    	    	
    	    	//list[mid]κ°€ target보닀 μž‘κ±°λ‚˜ κ°™μœΌλ©΄
    	    	if(target.compareTo(list.get(mid)) >= 0) begin = mid + 1;
    	    	else end = mid;
    	    }
    	    
    	    return begin;
    	}
    }
    λ°˜μ‘ν˜•
Designed by Tistory.