가장 긴 공통된 부분수열을 구하는 문제이다. 임이의 두 수열에서 각각의 부분 수열들 중 서로 같은 부분 수열을 의미한다.
백준 9251번 예시 를 보자
두 문자열 ACAYKP , CAPCAK 가 주어지고, LCS 는 ACAK이다. 이것을 어떻게 구할 것인가...
동적계획법( Dynamic programming) 을 통해서 푸는 과정을 설명해보자..
LCS문제의 겨우 부분수열에서 '순서'가 지켜지는 것이 포인트이다.
다음 표를 확인해보자.
다음 표를 채우는 기준은 다음과 같다.
각 가로 세로 문자까지의 수열들 중 LCS 공통 수열의 길이를 채택하는 것이다.
예를들어 (1,2)= (A,A) 를 생각해보자 'CAPCAK' 중 {C,A} 'ACAYKP' 중 { A,C,A} 의 중 겹치는 수열의 개수를 나타내는 것이다. 즉 2이다 {C,A}
이런 방식으로 표를 채우면 위 사진과 같아진다.
이 표를 확인해보면 다음과 같은 점화식을 세울수 있다..
x번째 원소와 y번째 원소가 같다면 (x-1,y-1)의 lcs길이의 +1 이된다.
만약 다르다면, Lcs ( x(i-1) y(j) ) 값 혹은 lcs (x(i),y(j-1))값들중 큰 값이 오면 된다.
LCS(Xi,Yi) : ( LCS( X(i-1) ,Y(j-1) ) +1 ) if(xi==yj)
LCS(Xi,Yi) : max( LCS(X(i-1), y(j) , LCS(X(i), y(j-1) ) if(xi!=yj)
( 도대체 이런 표랑 점화식 같은건 어떻게 찾을수 있는 건가.. ㅋㅋ ) 이렇게 완성된걸 보면 이해는 되는데. 처음 문제를보면 떠오르질 않는걸..
Top-Down 방식의 재귀형식으로 코드를 작성하면 다음과 같다.
static int LCS(int x ,int y ){
//인덱스가 0,0 재귀호출을 통해 x,y값이 -1 값들어오면 0으로 return해줘야함.
if(x==-1|| y==-1){
return 0;
}
if(dp[x][y]==null){
dp[x][y]=0;
//문자가 같은지 검사 점화식대로 코드작성하면된다.
if(str1[x]==str2[y]){
dp[x][y]=LCS(x-1,y-1)+1;
}
//다르다면
else{
dp[x][y] = Math.max(LCS(x - 1, y), LCS(x, y - 1));
}
}
return dp[x][y];
}
그렇게 해서 채워진 dp[x][y] x,y값의 각 문자열의 마지막 index값을 넣으면 원하는 LCS의 길이가 나온다.
LCS 알고리즘을 알고 있었다면 쉽게 풀었을 문제였을 것이다..
비슷한 문제를 여러번 풀어도 DP문제는 너무 어려운거 같다.. ㅋㅋ; 해설 안보고는 못풀정도
https://st-lab.tistory.com/139
늘 좋은 풀이를 주시는 블로그에 감사함을 표합니다..