균형 잡힌 괄호 표현식(balanced expression)이란 모든 종류의 괄호 쌍이 올바른 순서로 짝을 이루고 있는 식을 의미합니다. 즉, 여는 괄호마다 그에 대응하는 닫는 괄호가 정확한 순서({ }, [ ], ( ))로 존재해야 합니다.
몇 가지 예시를 통해 개념을 더 자세히 살펴보겠습니다.
표현식 – {([][]{})({}[]{})}
출력 – 균형 잡힘(balanced)
설명 – 모든 여는 괄호에 대해 그에 맞는 닫는 괄호가 존재하며, 짝을 이루는 괄호들이 안팎으로 올바르게 중첩되어 배치되어 있습니다.
표현식 – {(})
출력 – 균형 잡히지 않음(not balanced)
설명 – 순서가 어긋난 괄호 쌍이 존재하여 표현식 전체가 균형을 이루지 못합니다.
교체가 포함된 균형 표현식 문제란?
'교체가 포함된 균형 표현식(balance expression with replacement)' 문제에서는 '{', '}', '[', ']', '(', ')' 여섯 종류의 괄호로 이루어진 문자열이 주어집니다. 문자열의 일부 위치에는 괄호가 비어 있고 그 자리에 '*' 기호가 들어 있습니다. 우리의 과제는 모든 '*' 기호를 적절한 괄호로 교체했을 때, 주어진 표현식이 유효한 균형 잡힌 표현식이 될 수 있는지 확인하는 것입니다.
예시
입력 – exp = "{[*(*)]}"
출력 – 표현식은 균형을 이룰 수 있습니다.
설명 – 두 개의 '*' 기호를 교체하면 {[(())]}가 되어 균형이 맞습니다.
입력 – exp = "[(*){}{{}}]"
출력 – 표현식은 균형을 이룰 수 없습니다.
설명 – 하나의 '*' 기호를 어떤 괄호로 교체하더라도 표현식의 균형을 맞출 수 없습니다.
해결 접근 방법
문제를 명확히 이해했다면 이제 해결책을 세워볼 차례입니다. 주어진 괄호 표현식이 균형을 이루는지 확인하기 위해 스택(stack) 자료구조를 활용합니다.
이 작업을 수행하기 위한 연산 과정은 다음과 같습니다.
문자열 표현식의 모든 요소를 순회하며 아래 작업을 수행합니다.
여는 괄호('{', '[', '(')를 만나면 해당 요소를 스택에 push 합니다.
닫는 괄호('}', ']', ')')를 만나면 스택의 최상단(top) 요소를 pop 하여, 현재 마주친 닫는 괄호와 짝이 맞는 여는 괄호인지 확인합니다.
두 괄호가 서로 짝이 맞으면 표현식의 다음 요소로 넘어갑니다(1단계).
짝이 맞지 않으면 해당 표현식은 균형이 맞지 않습니다.
'*'를 만나면 여는 괄호일 수도 있고 닫는 괄호일 수도 있으므로 다음과 같이 처리합니다.
먼저 여는 괄호로 취급하여 스택에 push 한 뒤, 재귀 호출을 통해 다음 요소부터 매칭되는 닫는 괄호를 찾습니다. 결과가 거짓(false)이라면 다음 단계로 진행합니다.
닫는 괄호로 취급하여 스택의 top과 일치하는지 확인하고, 일치한다면 스택의 top을 pop 합니다.
'*'를 닫는 괄호로 취급했을 때 스택의 여는 괄호와 일치하지 않으면 균형이 맞지 않음을 반환합니다.
최종 결과에 따라 문장을 출력합니다.
이 알고리즘은 각 '*'마다 '여는 괄호'와 '닫는 괄호' 두 가지 경우를 모두 시도해야 하므로, '*'의 개수를 k라고 할 때 최악의 경우 O(2^k)의 시간 복잡도를 가집니다.
C++ 구현 예제
위에서 설명한 해결 방법을 바탕으로 프로그램을 작성해 보겠습니다.
#include <bits/stdc++.h>
using namespace std;
int isMatching(char a, char b){
if ((a == '{' && b == '}') || (a == '[' && b == ']') || (a == '(' && b == ')') || a == '*')
return 1;
return 0;
}
int isBalancedexpression(string s, stack<char> ele, int ind){
if (ind == s.length())
return ele.empty();
char topEle;
int res;
if (s[ind] == '{' || s[ind] == '(' || s[ind] == '[') {
ele.push(s[ind]);
return isBalancedexpression(s, ele, ind + 1);
}
else if (s[ind] == '}' || s[ind] == ')' || s[ind] == ']') {
if (ele.empty())
return 0;
topEle = ele.top();
ele.pop();
if (!isMatching(topEle, s[ind]))
return 0;
return isBalancedexpression(s, ele, ind + 1);
}
else if (s[ind] == '*') {
stack<char> tmp = ele;
tmp.push(s[ind]);
res = isBalancedexpression(s, tmp, ind + 1);
if (res)
return 1;
if (ele.empty())
return 0;
ele.pop();
return isBalancedexpression(s, ele, ind + 1);
}
return 0;
}
int main(){
string s = "{[*(*)]}";
stack<char> ele;
if (isBalancedexpression(s, ele, 0))
cout << "Balanced";
else
cout << "Not Balanced";
return 0;
}출력 결과
Balanced