Study/Problem Solving

[백준 / Java] 1062번: 가르침 (골드4)

Pearlii 2023. 9. 30. 00:14

문제 풀이 날짜: 2023.09.12
포스트 작성일: 2023.09.30

 

* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.

 

문제 출처

백준 온라인 저지 1062번: 가르침 (골드4)

 

키워드

순열, Next Permutation

 

풀이 접근법

  • 모든 단어는 anta로 시작해서 tica로 끝나기 때문에 이 단어에 들어가있는 알파벳 다섯개 a, n, t, i, c 가 우선순위를 갖는다.
  • 하지만 단순히 알파벳 출현 빈도수만을 체크한다면 아래와 같은 반례가 존재한다.
  • 이 테케에서 첫 번째 단어 빼고 다른 단어에는 x가 들어가지 않는다.
8 25
antaxxxxxxxxxxxxxxxxxxxtica
antaabctica
antadeftica
antaghijtica
antaklmntica
antaopqrtica
antastuvtica
antawxyztica

//x를 제외한 나머지 알파벳을 학습해야 함.
  • 이 상황에서 x의 빈도수가 가장 높다고 해도 다른 알파벳을 학습해야 최댓값을 구할 수 있다.
    • 따라서 a, n, t, i, c 다섯 개를 제외하고 어떤 알파벳을 학습해야 하는지조합으로 구한다.
    • 시간을 단축시키기 위하여 Next Permutation으로 풀이하였다.
  • 조합을 구할 때는, 현재까지 출현한 알파벳을 집계해서 각각의 인덱스를 Map으로 만들어 주었다.
    • 인덱스를 참조할 때는 Map을 이용해 O(1) 시간만에 이 알파벳의 학습 여부를 판단할 수 있다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

	private static int N;
	private static int K;
	private static int result = 0;

	private static int[] items;
	private static String[] words;
	private static Map<Character, Integer> indexes = new HashMap<>();
	private static Set<Character> set = new HashSet<>();

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		String[] temp = br.readLine().split(" ");

		N = Integer.parseInt(temp[0]);
		K = Integer.parseInt(temp[1]);
		words = new String[N];

		K = K - 5;
		if (K < 0) { // a, n, t, i, c 을 모두 학습할 수 없다면
			System.out.print(0);
			return;
		}

		for (int i = 0; i < N; i++) {
			words[i] = br.readLine();
			
			for (int j = 4; j < words[i].length() - 4; j++) {
				char c = words[i].charAt(j);
				
				if (c == 'a' || c == 'n' || c == 't'
						|| c == 'i' || c == 'c') { // a, n, t, i, c 제외
					continue;
				}
				
				set.add(c);
			}
		}
		
		int idx = 0;
		for(Character c : set) {
			indexes.put(c, idx++);
		}

		items = new int['z' - 'a' + 1 - 5];

		for (int i = items.length - 1; i >= items.length - K; i--) {
			items[i] = 1; // 뽑아야 하는 수만큼 초기화 시킨다.
		}

		do {
			result = Math.max(countWords(), result);
		} while (np(items));

		System.out.println(result);
	}

	private static int countWords() {
		int count = 0;
		all : for(String word : words) {
			for (int i = 4; i < word.length() - 4; i++) {
				char c = word.charAt(i);
				
				if (c == 'a' || c == 'n' || c == 't'
						|| c == 'i' || c == 'c') { // a, n, t, i, c 제외
					continue;
				}

				if(items[indexes.get(c)] == 0) {
					continue all; //읽을 수 없는 단어
				}
			}
			count++;
		}
		
		return count;
	}

	private static boolean np(int[] count) {
		int swapIdx = -1;
		for (int i = 1; i < count.length; i++) {
			if (count[i - 1] < count[i])
				swapIdx = i - 1;
		}
		if (swapIdx == -1)
			return false;

		int largerIdx = -1;
		for (int j = 1; j < count.length; j++) {
			if (count[swapIdx] < count[j])
				largerIdx = j;
		}

		swap(count, swapIdx, largerIdx);

		int j = count.length - 1;
		for (int i = swapIdx + 1; i < j; i++) {
			swap(count, i, j);
			j--;
		}

		return true;
	}

	private static void swap(int[] count, int swapIdx, int largerIdx) {
		int temp = count[swapIdx];
		count[swapIdx] = count[largerIdx];
		count[largerIdx] = temp;
	}
}

Next Permutation을 사용했음에도 동일 자바 대비 실행 시간이 조금 느리게 나왔다. 알파벳의 학습 여부를 boolean으로 체크하고, recursion으로 답을 찾으면 오히려 더 빠를 수도 있는데, 아무래도 Next Permutation으로 조합을 계산하는 시간을 줄였어도 컬렉션을 참조하는 시간이 오래 걸리는 듯하다.

 

git 링크

(git 링크 첨부)