| 1 | 9935 | 문자열 폭발 |
- 문자열이 폭발 문자열을 포함하고 있는 경우에, 모든 폭발 문자열이 폭발하게 된다. 남은 문자열을 순서대로 이어 붙여 새로운 문자열을 만든다.
- 새로 생긴 문자열에 폭발 문자열이 포함되어 있을 수도 있다.
- 폭발은 폭발 문자열이 문자열에 없을 때까지 계속된다.
- 폭발 문자열은 같은 문자를 두 개 이상 포함하지 않는다.
모든 폭발이 끝나고 남는 문자열을 구하는 문제
남아있는 문자가 없는 경우에는 "FRULA"를 출력
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String string = reader.readLine();
String burst = reader.readLine();
Stack<Character> stack = new Stack<>();
for(int i = 0; i < string.length(); i++) {
stack.push(string.charAt(i));
if(stack.size() >= burst.length()) {
boolean burstOut = true;
for(int j = 0; j < burst.length(); j++) {
if (stack.get(stack.size() - burst.length() + j) != burst.charAt(j)) {
burstOut = false;
break;
}
}
if(burstOut) {
for(int j = 0; j < burst.length(); j++) {
stack.pop();
}
}
}
}
if(stack.size() == 0) {
System.out.println("FRULA");
} else {
StringBuilder sb = new StringBuilder();
for(char c: stack) {
sb.append(c);
}
System.out.println(sb);
}
}
}
- 폭발이 일어나면 문자열이 재배치된다.
- 1차원 배열과 같은 자료구조에 저장하면 폭발 후에 인덱스의 공백이 생김. 문자를 하나씩 읽을 때마다 뒷 부분에서 폭발 문자열 포함 여부를 확인하고 제거할 수 있어야 함 -> stack
- 문자열이 재배치되어도 폭발 문자열이 포함될 수 있다 -> 반복 필요
- 스택의 top 부분에서 폭발 문자열 길이만큼 확인하고 폭발 문자열이라면 pop
| 2 | 17298 | 오큰수 |
크기가 N인 수열 A = A1, A2, ..., AN (N >= 1)이 있을 때, 수열의 각 원소 Ai에 대한 오큰수 NGE(i)
Ai의 오큰수는 오른쪽에 있으면서 Ai보다 큰 수 중에서 가장 왼쪽에 있는 수를 의미한다.
그러한 수가 없는 경우에 오큰수는 -1이다.
A = [3, 5, 2, 7]인 경우
NGE(1) = 5, NGE(2) = 7, NGE(3) = 7, NGE(4) = -1
A = [9, 5, 4, 8]인 경우
NGE(1) = -1, NGE(2) = 8, NGE(3) = 8, NGE(4) = -1이다.
*1차원 배열에 저장하고 O(n)=n^2 로 풀면 시간 초과
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(reader.readLine());
int[] seq = new int[N];
StringTokenizer st = new StringTokenizer(reader.readLine());
for(int i = 0; i < N; i++) {
seq[i] = Integer.parseInt(st.nextToken());
}
Stack<Integer> stack = new Stack<>();
int[] nge = new int[N];
for(int i = N - 1; i >= 0; i--) {
while(!stack.isEmpty()) {
if(seq[i] < stack.peek()) {
nge[i] = stack.peek();
stack.push(seq[i]);
break;
} else {
stack.pop();
}
}
if(stack.isEmpty()) {
nge[i] = -1;
stack.push(seq[i]);
}
}
StringBuilder sb = new StringBuilder();
for(int n : nge) {
sb.append(n).append(" ");
}
System.out.println(sb);
}
}
스택에 값을 저장
루프 1번에 nge를 구할 수 있지만 입력을 역순으로 읽고 저장해야 함
- NGE(i)는 i번째 수의 오른쪽에 있음
- 오른쪽에 있는 수 중 가장 큰 값보다도 자기가 더 크면 NGE(i)=-1
- 입력을 뒤에서부터 읽어서 nge의 가능성이 있는 수를 stack에 내림차 순으로 push
- input < stack.top 이면, nge = stack.top
- stack이 빌 때까지 조건을 만족하지 않는다면, nge = -1; stack.push(input)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(reader.readLine());
int[] seq = new int[N];
StringTokenizer st = new StringTokenizer(reader.readLine());
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < N; i++) {
seq[i] = Integer.parseInt(st.nextToken());
}
for(int i = 0; i < N; i++) {
while(!stack.isEmpty()) {
if(seq[stack.peek()] < seq[i]) {
seq[stack.pop()] = seq[i];
} else {
stack.push(i);
break;
}
}
if(stack.isEmpty()) {
stack.push(i);
}
}
while(!stack.isEmpty()) {
seq[stack.pop()] = -1;
}
StringBuilder sb = new StringBuilder();
for(int n : seq) {
sb.append(n).append(" ");
}
System.out.println(sb);
}
}
스택에 인덱스를 저장
마지막에 스택에 남은 인덱스에 대한 값을 -1로 지정해주는 작업 필요
- nge가 초기화 되지 않은 인덱스를 stack에 저장
- seq[stack.top] < seq[input] 이면, NEG(stack.top)=seq[input] 이고 top이 초기화 되었으므로 pop() => 반복
- 그렇지 않으면 인덱스를 stack에 push
| 3 | 17299 | 오등큰수 |
크기가 N인 수열 A = A1, A2, ..., AN이 있을 때, 수열의 각 원소 Ai에 대한 오등큰수 NGF(i)
Ai가 수열 A에서 등장한 횟수를 F(Ai) 라고 할 때, Ai의 오등큰수는 오른쪽에 있으면서 수열 A에서 등장한 횟수가 F(Ai)보다 큰 수 중에서 가장 왼쪽에 있는 수
그러한 수가 없는 경우에 오등큰수는 -1이다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(reader.readLine());
int[] seq = new int[N];
int[] f = new int[1000001];
StringTokenizer st = new StringTokenizer(reader.readLine());
for(int i = 0; i < N; i++) {
seq[i] = Integer.parseInt(st.nextToken());
f[seq[i]]++;
}
Stack<Integer> stack = new Stack<>();
int[] ngf = new int[N];
for(int i = N - 1; i >= 0; i--) {
while(!stack.isEmpty()) {
if(f[seq[i]] < f[stack.peek()]) {
ngf[i] = stack.peek();
stack.push(seq[i]);
break;
} else {
stack.pop();
}
}
if(stack.isEmpty()) {
ngf[i] = -1;
stack.push(seq[i]);
}
}
StringBuilder sb = new StringBuilder();
for(int n : ngf) {
sb.append(n).append(" ");
}
System.out.println(sb);
}
}
2번 문제에서 오등큰수를 판별하는 if절만 다르다.
if(f[seq[i]] < f[stack.peek()]) {
ngf[i] = stack.peek();
stack.push(seq[i]);
break;
} ...
| 5 | 12789 | 도키도키 간식드리미 |
스택을 활용하여 오름차순으로 학생을 빼내는 문제
- 입력의 첫째 줄에는 현재 승환이의 앞에 서 있는 학생들의 수 N(1 ≤ N ≤ 1,000,자연수)이 주어진다.
- 다음 줄에는 승환이 앞에 서있는 모든 학생들의 번호표(1,2,...,N) 순서가 앞에서부터 뒤 순서로 주어진다.
- 승환이가 무사히 간식을 받을 수 있으면 "Nice", 그렇지 않다면 "Sad"를 출력한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(reader.readLine());
// 현재 줄 서있는 곳
StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
// 한 명씩만 설 수 있는 공간
Stack<Integer> stack = new Stack<>();
// 찾는 번호표: i번
for(int i = 1; i <= n; i++) {
// stack 에서 i 찾기
if(!stack.isEmpty() && stack.peek() == i) {
stack.pop();
continue; // 찾으면 다음 번호표로
}
// token 에서 i 찾기
boolean result = false;
while(tokenizer.hasMoreTokens()) {
int number = Integer.parseInt(tokenizer.nextToken());
if(number == i) {
result = true;
break;
}
stack.push(number); // i가 아닌 수 -> stack에 저장
}
// stack 과 token 에서 i를 찾지 못한 경우 실패
if(!result) {
System.out.println("Sad");
return;
}
}
System.out.println("Nice");
}
}
stack.top에 새로운 값이 쌓이기 전에 먼저 탐색
처리해야 하는 번호표(i)와 stack.top 값이 다르다면 token 읽고 stack 채우기
성공 조건
처리해야 하는 번호표를 모두 소진 => for-loop 가 정상적으로 종료
실패 조건
처리해야 하는 번호표가 있는데 => for- loop 가 끝나지 않은 상태
한 명씩만 설 수 있는 공간(stack.top)에도 현재 줄 서있는 곳(token)에도 찾는 번호표가 없을 때
| 4 | 1725 | 히스토그램 |

각 칸의 간격은 일정하고, 높이는 어떤 정수로 주어진다. 위 그림의 경우 높이가 각각 [2 1 4 5 1 3 3]이다.
이러한 히스토그램의 내부에 가장 넓이가 큰 직사각형을 그리려고 한다. 아래 그림의 빗금 친 부분이 그 예이다. 이 직사각형의 밑변은 항상 히스토그램의 아랫변에 평행하게 그려져야 한다.

주어진 히스토그램에 대해, 가장 큰 직사각형의 넓이를 구하는 프로그램을 작성하시오.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(reader.readLine());
int[] arr = new int[n];
for(int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(reader.readLine());
}
Stack<Integer> stack = new Stack<>();
int maxArea = 0;
stack.push(0);
for(int i = 1; i < n; i++) {
while(!stack.isEmpty()) {
if(arr[stack.peek()] <= arr[i]) {
break;
}
int height = arr[stack.pop()];
int width = stack.isEmpty() ? i : i - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
stack.push(i);
}
while(!stack.isEmpty()) {
int height = arr[stack.pop()];
int width = stack.isEmpty() ? n : n - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
System.out.println(maxArea);
}
}
인접한 직사각형 2개의 넓이를 구할 때 둘 중 더 낮은 높이가 기준이 됨
즉, i번째 직사각형을 확장하려면 높이가 h(i)보다 크거나 같은 직사각형이 인접할 때 가능
1. 직사각형을 순차적으로 방문할 때 이전 위치(i-1)보다 현재 위치(i)의 높이가 낮으면 이전 위치까지의 직사각형의 넓이 계산
- 현재(i)보다 높이가 낮거나 같은 직사각형을 만날 때까지 또는 스택이 empty 상태가 될 때까지 pop하여 넓이를 계산하고 최댓값(maxArea) 비교
- 스택에 저장된 직사각형은 높이에 대한 오름차 순으로 정렬되어 있음 → 스택에 직사각형이 a b 순으로 저장되었다면 [ a, b 사이의 거리 * a의 높이 ] 의 넓이를 가지는 직사각형 생성 가능. 밑변에 해당하는 a, b 사이의 거리를 구하기 위해 스택에 직사각형의 위치(index)를 저장
2. 현재 위치(i)를 스택에 저장
3. 1, 2번을 n-1번 반복한 후, 스택이 empty 상태가 될 때까지 같은 과정 수행
'CT' 카테고리의 다른 글
| [BOJ] 30단계. 그래프와 순회 2 (0) | 2024.05.10 |
|---|---|
| [BOJ] 30단계. 그래프와 순회 1 (0) | 2024.05.07 |
| [BOJ] 28단계 동적 계획법 2 (0) | 2023.06.16 |
| [BOJ] 27단계 우선순위 큐 (0) | 2023.06.14 |
| [BOJ] 26단계 이분 탐색 (0) | 2023.05.17 |