괄호의 짝을 맞추는 문제는 아주 전형적인 Stack을 활용한 문제입니다. 여는 괄호가 나왔을 때 stack에 push하고 닫는 괄호가 나오면 stack에서 pop해가면서 짝을 맞추면 됩니다. pop했을 때 stack이 비어 있거나 짝이 맞는 괄호가 아니라면 올바른 괄호 문자열이 아닙니다. 마지막에 stack이 empty가 아닌 경우도 올바른 괄호 문자열이 아닙니다.
이 문제는 괄호를 회전하는 부분이 포함되어 있는데요. 이는 아주 간단한 문자열 조작으로 구현할 수 있습니다.
저는 닫는 괄호가 나왔을 때 짝을 맞추는 부분 (대, 중, 소 괄호 유형이 맞는지 확인하는 과정)의 코드가 복잡해질 것 같아서 미리 괄호의 유형을 enum으로 선언해두었습니다. 코드는 좀 더 길지만, 가독성이 높아졌다고 생각합니다.
// 괄호의 유형 (대, 중, 소)
enum PType {
case large, medium, small
}
// 괄호의 유형을 리턴해주는 extension
extension Character {
var pType: PType {
switch self {
case "[": return .large
case "]": return .large
case "{": return .medium
case "}": return .medium
case "(": return .small
case ")": return .small
default: return .large
}
}
}
// 올바른 괄호 문자열인지 확인하는 함수
func isRight(_ s: String) -> Bool {
// 여는 괄호
let open: [Character] = ["[", "{", "("]
var stack = [Character]()
// 괄호 문자열 전체를 순회
for c in s {
// 여는 괄호일 때 push
if open.contains(c) {
stack.append(c)
// 닫는 괄호라면 pop
} else {
// stack이 비어 있다면 false
if stack.isEmpty {
return false
}
let popped = stack.popLast()!
// pop한 괄호가 짝이 맞지 않다면 false
if popped.pType != c.pType {
return false
}
}
}
// 순회가 끝냈을 때 stack이 비어있을 때만 true
return stack.isEmpty ? true : false
}
func solution(_ s:String) -> Int {
var s = s
var ans = 0
// 괄호를 회전하는 함수
func moveLeft() {
let left = s.removeFirst()
s = s + String(left)
}
// 올바른 괄호 문자열인지 확인하고 회전시키면서 모든 회전 케이스에 대해 확인
for _ in 0..<s.count {
if isRight(s) { ans += 1 }
moveLeft()
}
return ans
}