📌 문제 링크 - https://www.acmicpc.net/problem/18119
문제
준석이는 영어 단어를 외우려고 한다. 사전에는 N가지 단어가 적혀 있다. 모든 단어는 소문자이다. 단어 안에 있는 모든 알파벳을 알 때, 그 단어를 완전히 안다고 한다.
다음과 같은 쿼리들이 주어진다.
- 1 x : 알파벳 x를 잊는다.
- 2 x : 알파벳 x를 기억해 낸다.
처음에 모든 알파벳을 기억하는 상태고, 모음은 완벽하게 외웠기 때문에 절대 잊지 않는다.
각 쿼리마다 완전히 알고 있는 단어의 개수를 출력하여라.
입력
첫 번째 줄에는 정수 N (1 ≤ N ≤ 104)과 M (1 ≤ M ≤ 5×104)이 주어진다.
다음 N개의 줄에는 문자열이 하나씩 주어진다. 문자열의 길이는 103을 넘지 않는다.
다음 M개의 줄에는 정수 o와 문자 x가 한 줄씩 주어진다. o는 1, 2중 하나이고, x는 알파벳 소문자이다.
o가 1이면 x를 잊는다는 뜻이고, o가 2면 x를 기억해낸다는 뜻이다. o가 1일 때는 x를 기억하고 있었음이 보장되고, o가 2일 때는 x를 잊고 있었음이 보장된다.
출력
각 쿼리마다 정수 하나를 출력한다.
풀이
비트마스킹을 사용해 풀 수 있는 문제다.
단어는 소문자로 구성되어 있기 때문에 'a' ~ 'z'까지 26비트로 표현 가능하다.
예를 들어 a는 0번째 비트, b는 1번째 비트
a -> (1 << 0) 0001
b -> (1 << 1) 0010
우선 각 단어에 사용된 알파벳에 해당하는 비트를 OR 연산을 통해 하나의 비트마스크로 배열(words)에 저장해둔다.
준석이는 맨 처음에 모든 알파벳을 기억하고 있으므로 잊어버린 알파벳이 없다는 뜻이며, 이 상태를 X = 0으로 표현할 수 있다.
이후 쿼리마다 준석이가 알파벳을 잊거나 기억하게 되면 X를 갱신해주고, 갱신 후 모든 단어를 순회하며 AND 연산을 통해 암기 가능한 단어의 개수를 세주면 된다.
if ((word & X) == 0)
이 조건이 참이라면 해당 단어에 준석이가 잊어버린 알파벳이 포함되지 않았다는 뜻이다.
word: 0b0010_0000_0000_0011
X : 0b0000_0000_0000_0110
------------------------------
& : 0b0000_0000_0000_0010 → 0이 아님. 준석이가 b, c를 잊었고, 해당 단어에는 b가 포함됨.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
// https://www.acmicpc.net/problem/18119
public class Main_18119 {
private static int X; // 준석이가 잊어버린 알파벳 집합
private static int[] words; // 각 단어가 사용하는 알파벳 집합
private static int[][] Q; // 쿼리
public static void main(String[] args) throws IOException {
init();
sol();
}//main
private static void sol() {
StringBuilder ans = new StringBuilder();
// 각 쿼리마다 완전히 알고 있는 단어의 개수를 출력하여라.
for(int[] q : Q) {
executeQuery(q);
int cnt = getCnt();
ans.append(cnt).append('\n');
}
System.out.print(ans);
}//sol
private static int getCnt() {
int cnt = 0; // 알고 있는 단어의 개수
for(int word : words) {
// 단어에 있는 알파벳 전부 기억
if((word & X) == 0) cnt++;
}
return cnt;
}//getCnt
private static void executeQuery(int[] q) {
int o = q[0]; // 명령 종류
int x = q[1]; // 알파벳
if(o == 1) X |= (1 << x); // o가 1이면 x를 잊는다
else X &= ~(1 << x); // o가 2면 x를 기억해낸다
}//executeQuery
private static void init() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken()); // 단어 개수
int M = Integer.parseInt(st.nextToken()); // 쿼리 개수
words = new int[N]; // 각 단어가 사용하는 알파벳 집합
Q = new int[M][2]; // 쿼리
for(int i=0; i<N; i++) {
char[] str = br.readLine().toCharArray();
int word = 0;
for(char c : str) {
word |= (1 << (c - 'a'));
}
words[i] = word;
}
for(int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
// o는 1, 2중 하나이고, x는 알파벳 소문자이다.
int o = Integer.parseInt(st.nextToken());
int x = st.nextToken().charAt(0) - 'a';
Q[i][0] = o;
Q[i][1] = x;
}
br.close();
}//init
}//class
'Algorithm > 백준' 카테고리의 다른 글
[Java] 백준 23030. 후다다닥을 이겨 츄르를 받자! (0) | 2025.06.16 |
---|---|
[Java] 백준 28107. 회전초밥 (0) | 2025.06.10 |
[Java] 백준 15990. 1, 2, 3 더하기 5 (0) | 2025.06.01 |
[Java] 백준 10713. 기차 여행 (0) | 2025.05.27 |
[Java] 백준 3687. 성냥개비 (1) | 2025.04.23 |