https://www.algospot.com/judge/problem/read/RESTORE

 

algospot.com :: RESTORE

실험 데이터 복구하기 문제 정보 문제 토요일에 출근해서 연구실에서 놀고 있던 대학원생 진호는 실수로 실험에 사용하던 데이터를 삭제하고 말았습니다. 복사본도 없는 터라 이대로라면 교수

www.algospot.com

코드설명

동적계획법(Dynamic Programming, DP) + 비트마스킹(BitMask) + 완전탐색(BruteForce) 를 활용합니다.

 

문제에서 어려웠던점은 reconstruct 함수로 최대값을 Construct, 즉, 실제로 출력하는 방법입니다.

next를 사용했을경우의 답이 restore했던 값과 같다면, 해당 단어를 선택하는 것이 최선의 선택입니다.

코드

package Main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	private static int C, N, W, H, K;
    private static int answer = 0;
    private static String[] word;
    private static int[][] cache, overlap;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        C = Integer.parseInt(st.nextToken());
        while(C-- > 0){
        	st = new StringTokenizer(br.readLine());
        	K = Integer.parseInt(st.nextToken());
        	
        	word = new String[K];
        	cache = new int[K][1<<K]; //왜 1<<N으로 하지? 순열 개수 아닌가? 3!
        	overlap = new int[K][K];
        	
        	for(int i=0;i<K;i++) {
        		Arrays.fill(cache[i], -1);
        	}
        	
        	for(int i=0;i<K;i++) {
        		st = new StringTokenizer(br.readLine());
        		word[i] = st.nextToken();
        	}
        	
        	precalcOverLap();
        	
//        	System.out.println();System.out.println();System.out.println();
//        	for(int i=0;i<K;i++) {
//        		for(int j=0;j<K;j++) {
//        			System.out.print(overlap[i][j]+" ");
//        		}
//        		System.out.println();
//        	}

        	String answer = "";
        	for(int i=0;i<K;i++) {
        		if(overlap[i][i] != -1) {
        			
        			String ret = word[i] + reconstruct(i, (1<<i));
        			
        			if(answer.equals("")) {
        				answer = ret;
        			} else if(answer.length() > ret.length()){
        				answer = ret;
        			}
        		}
        	}
        	System.out.println(answer);
        	
        	
        }
        
    }
    
    private static int restore(int last, int used) {
    	if(used == (1<<K) - 1) return 0;
    	
    	if(cache[last][used] != -1) return cache[last][used];
    	int ret = 0;
    	for(int next =0; next < K; next++) {
    		if( ((used & 1<<next) == 0) && overlap[last][next] != -1) {
    			int cand = overlap[last][next] + restore(next, used + (1<<next));
    			ret = Math.max(ret, cand);
    		}
    	}
    	return cache[last][used] = ret;
    }
    
    private static String reconstruct(int last, int used) {
    	//기저사례:
    	if(used == (1<<K) - 1) return "";
    	//다음에 올 문자열 조각을 찾는다.
    	for(int next = 0; next < K; next++) {
    		//next가 이미 사용되었으면 제외, overlao[last][used] 완전히 겹치는 경우가 존재한다면 무시.
    		if( ((used) & (1 << next)) > 0 || overlap[last][next] == -1) {
//    			System.out.println("continue because lastnext");
    			continue;
    		}
    		
    		//next를 사용했을경우의 답이 최적해와 같다면 next를 사용한다.
    		int ifUsed = restore(next, used + (1<<next)) + overlap[last][next];
    		if(restore(last, used) == ifUsed) {
    			return (word[next].substring(overlap[last][next]) + reconstruct(next, used + (1<<next)));
    		}
    	}
    	return "";
    }

    
    private static void precalcOverLap() {
    	for(int i=0; i<K; i++) {
    		for(int j=0;j<K;j++) {
    			if(i == j) continue;
    			if(overlap[i][j] == -1) continue;
    			//i번쨰 단어와 j번쨰 단어를 비교하자.
    			
    			//만약 하나의 문자열에 아예 포함되어 있다면, overlap[i][j] == -1로 바꾼다.
    			if(word[i].contains(word[j])) {
    				for(int h=0; h<K;h++) {
    					overlap[h][j] = -1;
    					overlap[j][h] = -1;
    				}
    				continue;
    			}
    			
    			
    			//1. 먼저 더 작가너 같은 길이의 단어가 해당 문자열을 순회하면서 일치하는지 찾을 것 이다.
    			//최대값을 반환해주어야 하니, len을 1부터가 아닌 겹칠 수 있는 최대길이부터 시작합니다.
    			for(int len = Math.min(word[i].length(), word[j].length()); len > 0; len--) {
    				if(word[i].substring(word[i].length() - len).equals(word[j].substring(0, len))) {
    					overlap[i][j] = len;
    					break;
    				}
    			}
    			
    		}
    	}
    }
}

+ Recent posts