0️⃣ 문제 링크
1️⃣ 문제 설명
괄호로 이루어진 문자열 s가 주어진다.
문자열을 왼쪽으로 회전시켰을 때, 올바른 괄호 문자열이 되는 경우의 수를 구하는 문제이다.
- 사용할 수 있는 괄호 종류:
(),[],{} - 회전 횟수는 문자열 길이만큼 가능
- 올바른 괄호 문자열의 개수를 반환
2️⃣ 문제 접근 방식
- 문자열 회전
- 문자열을 실제로 회전시키지 않고 똑같은 문자열을 연결해 사용
- 올바른 괄호 문자열 판별
Deque<Character>를 스택처럼 사용- 여는 괄호라면 push
- 닫는 괄호라면
- 스택이 비어 있으면 실패
- top과 짝이 맞지 않으면 실패
- 이 외의 경우는 poll하고 진행
시간 복잡도 : O(N²) → (N ≤ 1000 이므로 완전탐색 가능)
공간 복잡도 : O(N) → (스택 최대 크기)
3️⃣ 전체 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.*;
class Solution {
static String ss;
public int solution(String s) {
int answer = 0;
ss = s + s;
for (int i = 0; i < s.length(); i++) {
if (check(i)) answer++;
}
return answer;
}
static public boolean check(int x) {
Deque<Character> dq = new ArrayDeque<>();
for (int i = x; i < x + ss.length() / 2; i++) {
char c = ss.charAt(i);
if (c == '{' || c == '[' || c == '(') {
dq.offerLast(c);
} else {
if (dq.isEmpty()) return false;
char top = dq.peekLast();
if ((top == '{' && c == '}') || (top == '[' && c == ']') || (top == '(' && c == ')')) {
dq.pollLast();
} else {
return false;
}
}
}
if (!dq.isEmpty()) return false;
return true;
}
}
4️⃣ 개선된 코드
- 문자열 회전
(start + i) % n인덱싱을 사용해 회전 효과 구현- 불필요한 문자열 복사를 제거하여 공간 사용 최소화
- 최적화
- 문자열 길이가 홀수면 애초에 불가능 → 즉시 종료
- 회전 후 첫 문자가 닫는 괄호면 검사 생략
Deque를 매번 객체 생성하지 않고clear()로 재사용
(매번new ArrayDeque<>()→ GC 부담)
- 코드 개선
peekLast()호출 없애기 → 한 번만 pop하고 비교- 괄호 매칭 함수 분리 →
isMatch함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.*;
class Solution {
static Deque<Character> stack = new ArrayDeque<>();
public int solution(String s) {
int n = s.length();
if (n % 2 == 1) return 0; // 홀수 길이는 불가능
int answer = 0;
for (int start = 0; start < n; start++) {
char first = s.charAt(start);
// 닫는 괄호로 시작하면 바로 실패
if (first == ')' || first == ']' || first == '}') continue;
if (check(s, start, n)) answer++;
}
return answer;
}
static boolean check(String s, int start, int n) {
stack.clear();
for (int i = 0; i < n; i++) {
char c = s.charAt((start + i) % n);
if (c == '(' || c == '[' || c == '{') {
stack.offerLast(c);
} else {
if (stack.isEmpty()) return false;
char top = stack.pollLast();
if (!isMatch(top, c)) return false;
}
}
return stack.isEmpty();
}
static boolean isMatch(char open, char close) {
return (open == '(' && close == ')') ||
(open == '[' && close == ']') ||
(open == '{' && close == '}');
}
}
5️⃣ 회고 및 배운점
동일한 알고리즘임에도 불구하고, 객체 생성 최소화와 불필요한 연산 제거를 통해 실행 시간을 약 26.7% 단축 할 수 있었다.
완전탐색도 조건만 맞으면 충분히 좋은 선택이고, N이 작다면 억지로 복잡한 알고리즘을 쓰지 않아도 된다는 것을 다시 한 번 깨달았다.
문자열을 새로 만들기(복사)보다 인덱싱으로 처리하는 습관을 가져야겠다.