치춘짱베리굿나이스

[백준] 9251 본문

C C++/알고리즘풀이

[백준] 9251

치춘 2023. 8. 9. 22:47

LCS

문제

LCS(Longest Common Subsequence, 최장 공통 부분 수열)문제는 두 수열이 주어졌을 때, 모두의 부분 수열이 되는 수열 중 가장 긴 것을 찾는 문제이다.

예를 들어, ACAYKP와 CAPCAK의 LCS는 ACAK가 된다.

입력

첫째 줄과 둘째 줄에 두 문자열이 주어진다. 문자열은 알파벳 대문자로만 이루어져 있으며, 최대 1000글자로 이루어져 있다.

출력

첫째 줄에 입력으로 주어진 두 문자열의 LCS의 길이를 출력한다.

풀이

#include <iostream>
#include <string>
#include <algorithm>

int arr[1002][1002] = {0};

void printArr(int aLen, int bLen) {
    for (int i = 0; i <= aLen; i++) {
        for (int j = 0; j <= bLen; j++) {
            std::cout << arr[i][j] << " ";
        }
        std::cout << "\n";
    }
}

int main(void) {
    std::string a, b;
    int aLen, bLen;

    std::cin >> a >> b;

    aLen = a.length();
    bLen = b.length();

    for (int i = 1; i <= aLen; i++) {
        for (int j = 1; j <= bLen; j++) {
            if (a[i - 1] == b[j - 1]) {
                arr[i][j] = arr[i - 1][j - 1] + 1;
            } else {
                arr[i][j] = std::max(arr[i - 1][j], arr[i][j - 1]);
            }
        }
    }

    // printArr(aLen, bLen);
    std::cout << arr[aLen][bLen];
}

반성회

https://velog.io/@emplam27/알고리즘-그림으로-알아보는-LCS-알고리즘-Longest-Common-Substring와-Longest-Common-Subsequence

 

[알고리즘] 그림으로 알아보는 LCS 알고리즘 - Longest Common Substring와 Longest Common Subsequence

LCS는 주로 최장 공통 부분수열(Longest Common Subsequence)을 말합니다만, 최장 공통 문자열(Longest Common Substring)을 말하기도 합니다.

velog.io

이 블로그에 설명이 정말 잘 되어 있다… 감사합니다

어떻게 풀어야 할지 감도 못 잡고 있었는데 블로그 보고 이해함

기본 내용

ACAYKP
CAPCAK

두 문자열을 이용하여 최장 부분 문자열을 뜯어온다고 가정하자

 

첫 번째 줄은 0으로 초기화한다

아래에서도 나오겠지만 i - 1 인덱스에 접근하는 경우가 생기므로 음수 인덱스 접근 방지를 위해 채워주는 느낌이라고 생각하면 될 것

 

첫 행부터 채워나간다

  • AC가 만나는 부분 → AC의 비교 → LCS 없음
  • CC가 만나는 부분 → ACC의 비교 → LCS: C
  • AC가 만나는 부분 → ACAC의 비교 → LCS: C
  • YC가 만나는 부분 → ACAYC의 비교 → LCS: C
  • KC가 만나는 부분 → ACAYKC의 비교 → LCS: C
  • PC가 만나는 부분 → ACAYKPC의 비교 → LCS: C

  • AA가 만나는 부분 → ACA의 비교 → LCS: A
  • CA가 만나는 부분 → ACCA의 비교 → LCS: C 또는 A
  • AA가 만나는 부분 → ACACA의 비교 → LCS: CA
    • 이때 길이는 각각의 이전 문자열이었던 ACC의 LCS에서 1을 더한 값
  • YA가 만나는 부분 → ACAYCA의 비교 → LCS: CA
  • KA가 만나는 부분 → ACAYKCA의 비교 → LCS: CA
  • PA가 만나는 부분 → ACAYKPCA의 비교 → LCS: CA

부분 문자열이므로 LCS는 더 큰 값이 나오지 않는 이상 이전 값이 유지된다

 

 

  • AP가 만나는 부분 → ACAP의 비교 → LCS: A
  • CP가 만나는 부분 → ACCAP의 비교 → LCS: C 또는 A
  • AP가 만나는 부분 → ACACAP의 비교 → LCS: CA
  • YP가 만나는 부분 → ACAYCAP의 비교 → LCS: CA
  • KP가 만나는 부분 → ACAYKCAP의 비교 → LCS: CA
  • PP가 만나는 부분 → ACAYKPCAP의 비교 → LCS: CAP
    • 이때 길이는 각각의 이전 문자열이었던 ACAYKCA의 LCS에서 1을 더한 값

 

  • AC가 만나는 부분 → ACAPC의 비교 → LCS: A
  • CC가 만나는 부분 → ACCAPC의 비교 → LCS: AC
  • AC가 만나는 부분 → ACACAPC의 비교 → LCS: AC 또는 CA
  • YC가 만나는 부분 → ACAYCAPC의 비교 → LCS: AC 또는 CA
  • KC가 만나는 부분 → ACAYKCAPC의 비교 → LCS: AC 또는 CA
  • PC가 만나는 부분 → ACAYKPCAPC의 비교 → LCS: CAP

  • AA가 만나는 부분 → ACAPCA의 비교 → LCS: A
  • CA가 만나는 부분 → ACCAPCA의 비교 → LCS: AC
  • AA가 만나는 부분 → ACACAPCA의 비교 → LCS: ACA
    • 이때 길이는 각각의 이전 문자열이었던 ACCAPC의 LCS에서 1을 더한 값
  • YA가 만나는 부분 → ACAYCAPCA의 비교 → LCS: ACA
  • KA가 만나는 부분 → ACAYKCAPCA의 비교 → LCS: ACA
  • PA가 만나는 부분 → ACAYKPCAPCA의 비교 → LCS: ACA 또는 CAP

  • AK가 만나는 부분 → ACAPCAK의 비교 → LCS: A
  • CK가 만나는 부분 → ACCAPCAK의 비교 → LCS: AC
  • AK가 만나는 부분 → ACACAPCAK의 비교 → LCS: ACA
  • YK가 만나는 부분 → ACAYCAPCAK의 비교 → LCS: ACA
  • KK가 만나는 부분 → ACAYKCAPCAK의 비교 → LCS: ACAK
    • 이때 길이는 각각의 이전 문자열이었던 ACAYCAPCA의 LCS에서 1을 더한 값
  • PK가 만나는 부분 → ACAYKPCAPCAK의 비교 → LCS: ACAK
문자열 a (ACAYKP) 의 인덱스: i
문자열 b (CAPCAK) 의 인덱스: j 라고 가정
위 표의 배열은 arr이다

여기서 규칙이 2개가 등장하는데,

  • 같은 문자를 만날 경우 (a[i] == b[j]), 각각의 이전 문자열의 LCS에서 1 증가한 값을 갖는다
  • 오른쪽 아래 대각선으로 1 증가한다는 의미
    • arr[i][j] = arr[i - 1][j - 1] + 1
  • 다른 문자의 경우, 각각의 두 이전 문자열의 LCS 중 큰 값을 이어받는다
    • arr[i][j] = max(arr[i - 1][j], arr[i][j - 1])

문제가 됐던 코드

for (int i = 0; i < aLen; i++) { // 여기서 0으로 초기화하는 부분
    for (int j = 0; j < bLen; j++) { // 여기서 0으로 초기화하는 부분
        if (a[i] == b[j]) {
            arr[i][j] = arr[i - 1][j - 1] + 1; // 음수 인덱싱 되는 부분
        } else {
            arr[i][j] = std::max(arr[i - 1][j], arr[i][j - 1]); // 음수 인덱싱 되는 부분
        }
    }
}

계속 틀렸습니다가 나왔는데 인덱싱 문제였다

C에서는 배열에 음수 인덱스로 접근하면 undefined behavior이라 운이 좋으면 오류 없이 잘 넘어갈 수도 있지만 잘못 걸리면 오류가 발생할 수도 있다는 것

'C C++ > 알고리즘풀이' 카테고리의 다른 글

[백준] 1904  (0) 2023.08.12
[백준] 2097  (0) 2023.08.12
[백준] 13241  (0) 2023.08.08
[백준] 2161  (0) 2023.07.25
[백준] 1925  (2) 2023.07.15
Comments