문제
https://www.acmicpc.net/problem/3986
제출 코드 v1
final class StringStack {
private var characters: [Character] = []
init() {}
func push(_ char: Character) {
characters.append(char)
}
func pop() -> Character {
characters.removeLast()
}
func peek() -> Character? {
characters.last
}
func isEmpty() -> Bool {
characters.isEmpty
}
}
var goodWordCount: Int = Int(readLine()!)!
while let input = readLine() {
let storage = StringStack()
for char in input {
if let last = storage.peek() {
if char == last {
_ = storage.pop()
} else {
storage.push(char)
}
} else {
storage.push(char)
}
}
if !storage.isEmpty() {
goodWords -= 1
}
}
print(goodWords)
어떤 점을 고민했을까?
- 스택 이용 사고력우선 Stack을 Storage 개념으로 생각했고, 문자열을 순회하면서 같은 문자와 매칭되기를 기다리는 문자들을 Storage에 저장했습니다. 매칭된 아치형 곡선이 서로 겹치면 안된다는 전제와 문자열을 순회할 때 매칭을 기다리는 문자가 무엇이고 이전 문자가 무엇인지를 알아야 한다는 조건을 찾으니 LIFO 방식의 Stack 이용이 이해되었습니다.
- 문자가 저장되다가 같은 문자를 만나 매칭될 때 Storage에서 제거되기 때문에 계속해서 문자열을 순회하다가 매칭되는 문자를 만나도 “매칭된 아치형 곡선이 서로 겹치면 안된다는 전제”를 충족하게 되고, LIFO 방식이기 때문에 pop() 호출했을 때 무조건 이전 문자를 반환하게 되어 이전 문자를 알아야 한다는 풀이 조건도 충족됩니다.
- 애초에 스택을 이용한다는 힌트를 받았음에도 알고리즘이 떠오르지 않았습니다. 그래서 스택은 LIFO 방식이고 문자열을 하나씩 제거한다는 힌트까지 얻었을 때 비로소 구조가 떠올라서 작성하게 되었습니다.
- 좋은 단어 카운팅
- 좋은 단어의 경우 정상적으로 매칭되어 문자열 중 매칭을 기다리는 문자가 없을 것이기 때문에 전체 단어 개수에서 Stack의 isEmpty()를 통해 좋은 단어가 아님을 알게 되는 경우 -1씩 카운팅하여 좋은 단어 개수를 알 수 있도록 작성했습니다.
⏱️ 시간복잡도
- 단어마다 한번씩 순회하고 각 문자마다 Stack 연산을 수행하기 때문에, 스택의 기본 연산 시간복잡도 O(1)에서 단어마다 수행되기 때문에 전체 단어 개수를 N이라고 하여, 전체 시간 복잡도는 O(N)입니다.
📦 공간복잡도
- 저장공간 개념으로 Stack을 사용하기 때문에 최악의 경우 모든 문자가 쌓일 수도 있어 단어의 길이만큼 공간이 필요하여, 단어 하나당 O(n)의 공간 복잡도가 발생합니다.
- 위 코드에서 메모리 재할당 비용을 줄이고자 한다면, let storage = StringStack()을 while문 외부에서 선언하여 순회 후 초기화를 반복하는 코드로 작성할 수 있을 것 같습니다.
💨 실행속도
- 48ms
반응형
'알고리즘' 카테고리의 다른 글
| leetcode 706. Design HashMap (0) | 2025.04.10 |
|---|---|
| leetcode 2283. Check if Number Has Equal Digit Count and Digit Value 문제 풀이 (0) | 2025.04.09 |
| leetcode 70. Climbing Stairs 문제 풀이 (0) | 2025.04.07 |
| leetcode 225. Implement Stack using Queues 문제 풀이 (0) | 2025.04.04 |
| leetcode 232. Implement Queue using Stacks 문제 풀이 (0) | 2025.04.03 |