본문 바로가기

알고리즘 Algorithm/BOJ 백준 (초급~중급)

[BOJ 백준] 가르침(1062) Java

링크 : https://www.acmicpc.net/problem/1062

 

1062번: 가르침

남극에 사는 김지민 선생님은 학생들이 되도록이면 많은 단어를 읽을 수 있도록 하려고 한다. 그러나 지구온난화로 인해 얼음이 녹아서 곧 학교가 무너지기 때문에, 김지민은 K개의 글자를 가르칠 시간 밖에 없다.

www.acmicpc.net

 

문제 설명 : 

더보기

남극에 사는 김지민 선생님은 학생들이 되도록이면 많은 단어를 읽을 수 있도록 하려고 한다. 그러나 지구온난화로 인해 얼음이 녹아서 곧 학교가 무너지기 때문에, 김지민은 K개의 글자를 가르칠 시간 밖에 없다. 김지민이 가르치고 난 후에는, 학생들은 그 K개의 글자로만 이루어진 단어만을 읽을 수 있다. 김지민은 어떤 K개의 글자를 가르쳐야 학생들이 읽을 수 있는 단어의 개수가 최대가 되는지 고민에 빠졌다.

남극언어의 모든 단어는 "anta"로 시작되고, "tica"로 끝난다. 남극언어에 단어는 N개 밖에 없다고 가정한다. 학생들이 읽을 수 있는 단어의 최댓값을 구하는 프로그램을 작성하시오.

 

입력 : 

더보기

첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문자로만 이루어져 있고, 길이가 8보다 크거나 같고, 15보다 작거나 같다. 모든 단어는 중복되지 않는다.

 

출력 : 

더보기

첫째 줄에 김지민이 K개의 글자를 가르칠 때, 학생들이 읽을 수 있는 단어 개수의 최댓값을 출력한다.

 

예제 입력 : 

더보기

3 6 antarctica antahellotica antacartica

 

예제 출력 : 

 

 

접근법 : 

1) 어떻게 풀 것인가?

N과 K가 모두 매우 작은 편이다. (50이하) -> 완전탐색이 가능할 확률이 높다.

 

K개의 알파벳 조합으로 단어를 구성할 수 있는지를 묻는 문제이기에 "조합"을 구현할 수 있어야하고

완전탐색 + 백트래킹 방식으로 접근이 가능하다. 개인적으로 조합 문제는 DFS를 선호.

 

기본적인 조합 DFS에 마지막에 개수를 체크하는 함수만 구현한다면 답을 구할 수 있다.

 

참조 : DFS (깊이 우선 탐색)

https://ko.wikipedia.org/wiki/%EA%B9%8A%EC%9D%B4_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89

 

깊이 우선 탐색 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 깊이 우선 탐색긔 애니메이션 예시 깊이 우선 탐색( - 優先探索, 영어: depth-first search, DFS)은 맹목적 탐색방법의 하나로 탐색트리의 최근에 첨가된 노드를 선택��

ko.wikipedia.org

 

2) 시간복잡도

K 최대 26으로 가능한 최대 조합 숫자는 26C13 (26 Combination 13) → 약 1,000만

단어의 개수 50개로 최악의 경우에도 큰 무리없을 것으로 예상

(Java 실행시간 240ms)

 

3) 공간복잡도

알파벳 조합 26개는 boolean, 아무리 int로 해도 Bytes 단위이며, 단어 조합 역시 50개의 String (최대 길이 15)이므로

굳이 계산할 필요가 없을정도로 여유있음. 

 

4) 풀면서 놓쳤던점

- K가 5미만일때, 절대 불가능한 경우이므로 답인 0을 출력하고 main함수를 종료시켰는데, 

  bw.flush(); 와 bw.close(); 를 해주지 않아 오답이 발생함.

 → 기본 입출력 다시 한 번 숙지할 필요 있음

 

- DFS 진행시 가능한 K를 모두 사용하고 개수를 체크하는 로직 전에

  id의 범위를 넘어가면 return 시키는 로직을 위치시켜서 오답이 발생함.

 

 

5) 이 문제를 통해 얻어갈 것

삼성 SW역량테스트 A형(Advanced) 2문제 중 쉬운 문제보다 조금 더 쉬운 난이도로 예상.

 

조합, DFS, 백트래킹을 연습할 수 있으며, 함수를 나누는 연습을 하기에도 적절함.

기회가 된다면 alphabet 체크하는 상황에서 비트 마스킹을 통해서도 가능할 것 같다.

 

삼성 SW역량테스트 C형은 bit masking, little endian, big endian 등 computer science 적인 내용을 기본적으로 

알아야 문제 해결에 도움이 되기에 다음에 bit masking 방식으로 풀어봐야겠다.

 

 

Java 코드 : 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

// 가르침 백준 BOJ 1062

public class Main {
	
	static int N, K, sol;
	static int possible;
	static String[] words;
	static boolean[] alpha;
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		
		StringTokenizer st = new StringTokenizer(br.readLine());
	
		sol = 0;
		
		N = Integer.parseInt(st.nextToken());
		K = Integer.parseInt(st.nextToken());

		// K가 5미만일 경우 절대 단어를 읽을 수 없음 (a,c,i,n,t 필수) 
		if(K<5) {
			bw.write(String.valueOf(sol));
			br.close();
			bw.flush();
			bw.close();
			return;
		}		
		
		// 불필요한 접두사, 접미사 제외하고 words[] 에 저장
		words = new String[N];
		String word; 
		for(int i=0; i<N; i++) {
			word = br.readLine();
			words[i] = word.substring(4, word.length()-4);
		}
				
		// 필수 알파벳인 a,c,i,n,t를 true로 마킹, 마킹 가능한 숫자 한도를 계산
		alpha = new boolean[26];
		alpha['a'-'a'] = true;
		alpha['c'-'a'] = true;
		alpha['i'-'a'] = true;
		alpha['n'-'a'] = true;
		alpha['t'-'a'] = true;
		possible = K-5; 

		// DFS를 통해 배울 alphabet '조합'을 선택 후 
		// DFS 내부에서 배울 수 있는 단어 개수 체크   
		dfs(1,0);	// 'b'의 id인 1부터 시작, 현재 cnt 0 <= possible 까지 dfs 실행

		
		bw.write(String.valueOf(sol));
		
		br.close();
		bw.flush();
		bw.close();
	}
	
	//DFS로 알파벳 조합 생성
	static void dfs(int id, int cnt) {
		// 가능한 알파벳을 모두 사용했을때만 체크
		if(cnt==possible) {
			int result = wordCnt();
			sol = result > sol? result : sol;
			return;
		}
		// id가 알파벳 개수 26개를 넘어갈 경우 종료
		if (id>=26) return;
		// 아직 사용하지 않은 알파멧일때만 dfs로 확인
		if (alpha[id]==false) {
			alpha[id] = true;
			dfs(id+1, cnt+1);
			alpha[id] = false;
		}
		// 현재 알파벳을 pass하고 dfs 진행
		dfs(id+1, cnt);
	}

	// 단어 개수 체크 함수
	static int wordCnt() {
		String word;	    // 단어 
		boolean tFlag;	    // 단어별 가능여부 체크 flag
		int result = 0;		// 현재 알파벳 조합으로 가능한 최종 결과값
		
		for(int i=0; i<N; i++) {
			tFlag = true;
			word = words[i];
			int len = word.length();
			for (int j=0; j<len; j++) {
				char check = word.charAt(j);
				if (alpha[check-'a']==false) {
					tFlag = false;
					break;
				}
			}
			if (tFlag) result++;
		}
		return result;
	}
}
반응형