알고리즘/이론

최장 공통 부분 수열 (LCS problme)

만두맨두 2022. 12. 20. 20:33

Long Common Subsequence

한 개 이상의 문자열이 주어질 때,

중간에 다른 문자가 들어가더라도

공통되면서 가장 긴 부분 문자열을 찾는 문제

 

다이나믹 프로그래밍(DP, Dynamic Programming)을 사용해야 한다.

 

EX ) A와 B 두 개의 문자열이 주어진다.

         => A = 'ACAYKP' / B = 'CAPCAK'

 

1 ) ( 문자열의 길이 + 1 ) 만큼 2차원 배열을 생성한다.

      단, 인덱스가 0인 부분은 0으로 채워넣는다.

  - A C A Y K P
- 0 0 0 0 0 0 0
C 0            
A 0            
P 0            
C 0            
A 0            
K 0            

 

2) 한 글자씩 비교한다.

  - A C A Y K P
- 0 0 0 0 0 0 0
C 0 0          
A 0            
P 0            
C 0            
A 0            
K 0            

   - 글자가 같지 않다면, 위의 값왼쪽 값큰 값을 삽입한다.

      = 글자가 같지 않다는 것은 전에 찾은 공통 수열 부분을 그대로 가져간다는 것을 의미한다.

         전에 찾은 공통 수열 부분은 위의 값왼쪽 값뿐이며, 최장 공통 부분이기 때문에 큰 값을 가져오면 된다.

  - A C A Y K P
- 0 0 0 0 0 0 0
C 0 0 1        
A 0            
P 0            
C 0            
A 0            
K 0            

   - 글자가 같다면, 왼쪽 위 대각선에 1을 더한 값을 삽입한다.

      = 왼쪽 위 대각선(A)과 현재 위치의 문자열(AC/C)을 비교할 때의 달라진 점은

         두 문자열 모두 현재 위치의 문자(C)가 추가되었다는 점이다.

         때문에 왼쪽 위 대각선에 1을 더한 값을 가져오면 된다.

 

❗점화식 ❗

( A[i] === B[j] ) graph[i][j] = graph[i - 1][j - 1] + 1

( A[i] !== B[j] ) graph[i][j] = Math.max( graph[i][j - 1], graph[i - 1][j] )

 

3) 최종 배열은 다음과 같다.

  - A C A Y K P
- 0 0 0 0 0 0 0
 C 0 0 1 1 1 1 1
A 0 1 1 2 2 2 2
P 0 1 1 2 2 2 3
C 0 1 2 2 2 2 3
A 0 1 2 3 3 3 3
K 0 1 2 3 3 4 4

 

자바스크립트 코드

const fs = require('fs');
const filePath = process.platform==='linux' ? '/dev/stdin':'input.txt';
const [a, b] = fs.readFileSync(filePath).toString().split('\n').map((v) => v.trim());

const [n, m] = [a.length, b.length];
let graph = Array.from(Array(n + 1), () => Array(m + 1).fill(0));

for (let i = 1; i <= n; i++) {
  for (let j = 1; j <= m; j++) {
    if (a[i - 1] === b[j - 1]) {
      graph[i][j] = graph[i - 1][j - 1] + 1;
    } else {
      graph[i][j] = Math.max(graph[i][j - 1], graph[i - 1][j]);
    }
  }
}

console.log(graph[n][m]);