기본적인 DP 전략은 LCS 랑 비슷하다. 비교할 문자열이 두개이므로 2차원 DP 를 만들고 건너뛰어서 도달할 수 있는 이전 DP 값이 있으면 그 값을 이용해 점화식을 구성한다.
이때 사전순에 따른 문자열 구성의 편의를 위해 문자열 끝부분부터 점화식을 돌리는게 좋다. 그래야지 문자열 맨 앞부분의 dp[][] 값이 K-LCS 의 길이가 되어 공통서열의 앞부분을 문자열 구성의 시작점으로 잡기 편하기 때문이다.
주어진 문자열의 길이가 N = 1000 이고 k<=30 이므로 O(N^2 * K) 로 구성해도 된다. 하지만 Sliding Window 기법을 이용해 O(N^2 * K) 로 줄일 수도 있다.
편의상 이차원 dp[row][col] 에서 Row 를 A 문자열 위치로 두고 Col 을 B 문자열 위치라고 두자. 그러면 아래 코드는 Column 부터 먼저 검사한다고 볼 수 있다. 이때 Row 가 바뀌지 않는 한 같은 Column 의 연속된 k 개의 Dp 값, 즉 dp[a+(1~k+1)][b] 중의 최적값은 재사용 될 수 있다. deque 을 이용해 K 개의 Dp 값을 유지할 수도 있지만 크기가 크지 않으므로 아래 코드는 문자열크기의 배열에 값을 저장했다.
사전순에 따른 문자열 구성
문제의 핵심이 되는 부분이다. 실수하기가 매우 좋다.
나는 dp[][] 에 다음 dp[][] 상의 위치를 기록해서 타고가는 그리디 전략을 처음에 사용하려고 했다. 하지만 이는 성립하지 않는데, 왜냐하면 바로 이전 위치의 문자가 같으면 그 이전의 문자를 다시 봐야하기 때문이다. 바로 직전 문자만으로는 판단할 수 없는 경우가 생긴다.
그래서 모든 경우를 세야하는데 이때 사용하는 것이 BFS 이다. 직전 문자가 같은 경우들을 queue 에 넣어서 모든 경우를 셈하는 것이다. 구현은 의외로 간단한데 매 Node 마다 완전탐색을 돌리고, 가장 사전순으로 빠른 Node 들만 다시 queue 에 넣어서 BFS 를 돌리는 것이다.
댓글남기기