BOJ 7575

바이러스

문제출처

문제 해석

  • 바이러스에 감염된 프로그램 N개가 주어졌을 때, 바이러스 코드로 추정되는 부분이 있는지 판단하는 문제입니다.
  • 공통으로 K길이 이상 같은 코드가 바이러스 코드로 추정되는 부분입니다.
  • 또한, 바이러스 코드는 반대로 기입되었을 수도 있습니다.

설계(20)

  • 공통되는 코드가 있는지 찾는 문제입니다.
  • 길이 K 초과의 경우는 고려할 필요가 없습니다. 이유는 그보다 짧은 K길이가 공통되는 것을 만족해야 K초과가 공통될 수도 있기 때문입니다.
  • 따라서 길이 K가 공통인지 여부만 판단합니다.
  • 첫번째 프로그램에서 길이 K가 나올 수 있는 모든 경우를 조사합니다.O(M)
  • 정해진 길이 K에 대해서 모든 N개의 프로그램이 공통인지 판단하기 위해서는 일반적으로 O(NM)의 시간복잡도가 필요합니다.
  • 이를 최적화하기 위해서 O(N+M)시간복잡도가 필요한 KMP알고리즘을 이용합니다.
  • 반대로 된 코드도 고려해야하기 때문에 만약 기존 프로그램에서 공통된 바이러스 코드를 찾지 못했으면, 뒤집어서 한번 더 확인해줍니다.

구현(60)

코드
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MAXN 100
#define MAXM 1001

vector<int> arr[MAXN];
int pi[MAXM][MAXM];
int N,K;
int min_len = 1000;
void getpi(int start){
    int j = start;
    for (int i = start+1; i <= start+K; i++){
        while (j>start && arr[0][i] != arr[0][j]) j = pi[start][j-1];
        if (arr[0][i] == arr[0][j]){
            pi[start][i] = ++j;
        }
    }

}
bool kmp(int start){
    int cnt = 0;
    for (int k = 1 ; k< N; k++){
        bool flag = false;
        for (int kk = 0 ; kk<2&&!flag; kk++){
            int j = start;
            if(kk) reverse(arr[k].begin(),arr[k].end());
            for (int i = 0; i < arr[k].size();i++){
                while (j>start && arr[k][i]!=arr[0][j]) j =pi[start][j-1];
                if (arr[k][i] == arr[0][j]){
                    if (j == start+ K-1){
                        cnt++;
                        flag = true;
                        break;
                    }
                    else j++;
                }
            }
            if(kk) reverse(arr[k].begin(),arr[k].end());
        }
    }
    return cnt==N-1;
}
bool is_possible(){
    // 바이러스 코드가 k개인 경우 모두 조사
    for (int i= 0; i <=arr[0].size()-K; i++){
        getpi(i);
        if (kmp(i)) return true;
    }
    return false;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N >> K;
    for (int i = 0 ;i < N; i++){
        int m;
        cin >> m;
        min_len = min(min_len,m);
        arr[i] = vector<int>(m,0);
        for (int j = 0;j<m; j++){
            cin >> arr[i][j];
        }
    }
    bool ans = is_possible();
    if (ans) cout <<"YES\n";
    else cout <<"NO\n";
    return 0;
}
디버깅
  • 실패함수와 kmp알고리즘을 구현할 때, 기준이 되는 첫번째 프로그램의 인덱스를 모두 0부터 시작하는 실수를 하였습니다.
  • 첫번째 프로그램이 길이 K를 만족하는 부분을 start로 표시해주었으니, start부터 시작해야했습니다.
제출결과
  • 260 ms

마무리

  • KMP알고리즘을 활용한 문제였습니다. 공통된 길이 K인 부분만 확인해주면 되는것이 키포인트였습니다.