190102 스택&큐 (1) 쇠막대기
스택&큐 (1) 쇠막대기
문제 설명
여러 개의 쇠막대기를 레이저로 절단하려고 합니다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자릅니다. 쇠막대기와 레이저의 배치는 다음 조건을 만족합니다.
- 쇠막대기는 자신보다 긴 쇠막대기 위에만 놓일 수 있습니다.
- 쇠막대기를 다른 쇠막대기 위에 놓는 경우 완전히 포함되도록 놓되, 끝점은 겹치지 않도록 놓습니다.
- 각 쇠막대기를 자르는 레이저는 적어도 하나 존재합니다.
- 레이저는 어떤 쇠막대기의 양 끝점과도 겹치지 않습니다.
아래 그림은 위 조건을 만족하는 예를 보여줍니다. 수평으로 그려진 굵은 실선은 쇠막대기이고, 점은 레이저의 위치, 수직으로 그려진 점선 화살표는 레이저의 발사 방향입니다.
이러한 레이저와 쇠막대기의 배치는 다음과 같이 괄호를 이용하여 왼쪽부터 순서대로 표현할 수 있습니다.
- 레이저는 여는 괄호와 닫는 괄호의 인접한 쌍 ‘()’으로 표현합니다. 또한 모든 ‘()’는 반드시 레이저를 표현합니다.
- 쇠막대기의 왼쪽 끝은 여는 괄호 ‘(‘로, 오른쪽 끝은 닫힌 괄호 ‘)’로 표현됩니다.
- 위 예의 괄호 표현은 그림 위에 주어져 있습니다.
쇠막대기는 레이저에 의해 몇 개의 조각으로 잘리는데, 위 예에서 가장 위에 있는 두 개의 쇠막대기는 각각 3개와 2개의 조각으로 잘리고, 이와 같은 방식으로 주어진 쇠막대기들은 총 17개의 조각으로 잘립니다.
쇠막대기와 레이저의 배치를 표현한 문자열 arrangement가 매개변수로 주어질 때, 잘린 쇠막대기 조각의 총 개수를 return 하도록 solution 함수를 작성해주세요.
풀이 과정
- 막대기 내부에 포함된 레이저 개수가 n개라면 막대 조각은 n+1개가 된다.
-
막대와 레이저의 위치를 계산하고, 막대 내부에 포함되는 레이저 수를 구해 더한다.
- (을 마주치면 스택에 넣고, )을 마주쳤을때 pop
- 길이가 1 이라면 레이저 배열에 레이저 시작점(‘(‘의 위치)를 넣는다.
- 길이가 1 이상이라면 막대 리스트(시작점 ‘(‘의 인덱스와 끝점 ‘)’의 인덱스 가짐)에 넣는다
import java.util.*;
public class Stick{
int sPoint;
int ePoint;
public Stick(int sPoint,int ePoint){
this.sPoint=sPoint;
this.ePoint=ePoint;
}
public int getSPoint(){
return this.sPoint;
}
public int getEPoint(){
return this.ePoint;
}
}// 막대의 시작점과 끝점을 가지고 있는 객체
class Solution {
ArrayList<Integer> laser=new ArrayList<>(); // 레이저 시작 인덱스를 가진 배열
ArrayList<Integer> stack=new ArrayList<>(); // 막대 위치 계산을 위한 스택
ArrayList<Stick> stick=new ArrayList<>(); // 막대의 시작점과 끝점을 가지는 객체의 리스트
public int solution(String arrangement) {
int answer=0;
for(int i=0;i<arrangement.length();i++) {
if(arrangement.charAt(i)=='(') {
stack.add(i);
}else if(arrangement.charAt(i)==')'){
int sp=pop();
int ep=i;
if(ep-sp==1) {
laser.add(sp);
}// 만약 길이가 1이라면 레이저 시작점을 레이저 배열에 넣는다
else {
Stick newStick=new Stick(sp,ep);
stick.add(newStick);
}// 길이가 1이 아니라면 새로운 막대이므로 stick list에 넣는다
}
}// 문자열 연산을 통해 레이저와 막대를 구분하는 과정
for(int i=0;i<stick.size();i++) {
int sp=stick.get(i).getSPoint();
int ep=stick.get(i).getEPoint();
int cutLaser=0;
for(int idx:laser) {
if(idx>sp && idx<ep) {
cutLaser++;
}// 막대 안에 포함된 레이저의 개수를 구한다.
}
answer=answer+cutLaser+1;
// 막대 내부에 포함된 레이저의 개수가 n개이면 잘린 막대조각은 n+1개이다.
}// 막대마다 잘려지는 개수를 구하고, answer에 그 값을 더한다.
return answer;
}
public int pop(){
int element=stack.get(stack.size()-1);
stack.remove(stack.size()-1);
return element;
};// stack의 pop연산 구현
}
더 생각해볼것
-
정확도는 100이었으나 예상보다 실행 시간이 오래 걸렸다 (4461.27ms, 52MB)
- Stick을 객체로 만들었기 때문일까? 더 빠른 방법은?
-
막대가 아닌 레이저 끝점 위치 기준으로 생각하면 더 빠르다. 예를 들어 위의 막대 그림에서 인덱스 6의 첫 레이저를 만났을 때 스택의 사이즈는 3이고, 이 위치에서 막대를 자르면 3조각이 나오게 된다.
import java.util.Stack;
class Solution {
public int solution(String arrangement) {
int answer = 0;
Stack<Integer> stack = new Stack<>();
for (int i=0; i < arrangement.length(); i++) {
if (arrangement.charAt(i) == '(') {
stack.push(i);
} else {
stack.pop();
if (arrangement.charAt(i-1) == '(') {
answer += stack.size();
} else {
answer += 1;
}
}
}
return answer;
}
}
- 자바 컬렉션 프레임워크에 Stack이 있다는 점을 잊고 있었다(…완전 코찔이 티내는중 ㅜㅜ) 조만간 컬렉션 프레임워크에 대해 한번 쭉 정리할 예정!