Post

baekjoon 1023:괄호 문자열

baekjoon 1023 괄호 문자열

1023번 괄호 문자열

접근

dp(frontOpen, empty) = 지금까지 닫히지 않은 “(“가 frontOpen개 있고 채워야 하는 빈칸이 empty개
있을 때 경우의 수이다.
dp(frontOpen, empty) = dp(frontOpen+1, empty-1) //”(“추가한 경우
dp(frontOpen-1, empty-1) //”)”추가한 경우
그리고 skip(frontOpen, empty)에선 dp(frontOpen+1, empty-1)와 비교한다.
skip < dp(frontOpen+1, empty-1)이면 “(“붙이고
아니면 “)”붙이기

코드

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//1023
#include <array>
#include <iostream>
using namespace std;
array<array<long long int, 51>, 51> cache;
long long int noneParenStr(int frontOpen, int empty);
string skip(int frontOpen, int empty, long long int k);
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  long long int k;
  cin >> n >> k; // 1<= n <= 50, 0<= k <= 2^50-1
  // k자체가 skip개수라 1 빼거나 그럴 필요 없음
  for (int i = 0; i < 51; i++) {
    fill(cache[i].begin(), cache[i].end(), -1);
  }
  cout << skip(0, n, k) << "\n";
}
string skip(int frontOpen, int empty, long long int k) {
  // cout << frontOpen << " " << empty << " " << k << "\n";
  if (empty == 0) {
    return "";
  }
  if (noneParenStr(frontOpen, empty) <= k) {
    // cout << noneParenStr(frontOpen, empty) << " " << k << "\n";
    return "-1";
  }
  if (frontOpen >= 0 && noneParenStr(frontOpen + 1, empty - 1) <= k) {
    return ")" + skip(frontOpen - 1, empty - 1,
                      k - noneParenStr(frontOpen + 1, empty - 1));
  } else if (frontOpen < 0) {
    if (noneParenStr(frontOpen, empty - 1) <= k) {
      return ")" +
             skip(frontOpen, empty - 1, k - noneParenStr(frontOpen, empty - 1));
             //frontOpen을 굳이 빼지 않는다. 어짜피 음수다.
    } else {
      return "(" + skip(frontOpen, empty - 1, k);
            //frontOpen을 더하지 않아야한다. 여기선 frontOpen을 더해서 양수가 되버려도 실질적으론  
            //")("이런 모양이 되서 paren이 될 수 없다. 
    }
  } else {
    return "(" + skip(frontOpen + 1, empty - 1, k);
  }
}
long long int noneParenStr(int frontOpen, int empty) {
  if (frontOpen == 0 && empty == 0)
    return 0; //paren
  if (frontOpen > 0 && empty == 0)
    return 1; //noneparen
  if (frontOpen < 0)
    return (long long int)1 << empty; //이 에러 어케 찾냐...
                                      //frontOpen이 음수가 됐다는 건 ")"가 더 많다는 것이고 그 
                                      //이제 noneParen이 확정이다. 
  long long int &ret = cache[frontOpen][empty];
  if (ret != -1)
    return ret;
  ret = noneParenStr(frontOpen - 1, empty - 1) +
        noneParenStr(frontOpen + 1, empty - 1);
  return ret;
}

배운 점

많이 어려운 dpSkip 문제였다. 일단 가장 당연하게 괄호ㄴㄴ문자열의 개수를 dp로 구해야 할 것이고
dp로 구한다면 하나씩 채워나가는 방식이었을 거고 case분류 하다보면 자연스럽게 열려있는 괄호의 개수가
중요함을 깨달을 것이다. 몇 개를 채워야 한다는 것은 앞으로 채울 것을 보다보면 자연스럽게 필요하다.

This post is licensed under CC BY 4.0 by the author.