[백준] 12891번 DNA 비밀번호 (java)

0

코딩테스트

목록 보기
7/37
post-thumbnail

<문제>

문제 출처 : https://www.acmicpc.net/problem/12891

<나의 풀이>

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    static int[] myArr;
    static int[] checkArr;
    static int checkSecret; 
    
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(bf.readLine());

        // 1. 필요한 값을 받아주기 위한 변수 초기화
        int S = Integer.parseInt(st.nextToken());   // 받을 문자열의 길이
        int P = Integer.parseInt(st.nextToken());   // 비밀번호의 길이
        checkArr = new int[4];   // 비밀번호를 만들 조건을 담아주는 배열
        myArr = new int[4];   // 슬라이딩으로 받아온 문자들의 갯수를 담아주는 배열
        char A[] = new char[S]; // 전체길이 문자를 담아주는 배열
        checkSecret = 0;    // 현재 문자 중 몇개가 조건을 충족하는지 체크해주는 변수, 비밀번호 조건을 모두 충족하면 4가되고 result값이 1오른다.
        int result = 0; // 조건을 충족하는 경우의 수
        // 2. 문자열의 전체길이를 받아 char 배열로 바꿔줌
        A = bf.readLine().toCharArray();
        // 3. 비밀번호 조건을 받아줌.
        st = new StringTokenizer(bf.readLine());
        for (int i = 0; i < 4; i++) {
            checkArr[i] = Integer.parseInt(st.nextToken());
            if (checkArr[i] == 0) checkSecret++;    // 만약 필요한 문자열이 아니라면 비밀번호를 만들 떄 고려하지 않기 위함.
        }
        // 4. 부분 문자열 처음 받아 세팅
        for (int i = 0; i < P; i++) {
            Add(A[i]);
        }
        if (checkSecret == 4) result++; // 처음 받아온 문자열에서 조건을 충족하는지 확인

        // 5. 슬라이딩 윈도우
        for (int i = P; i < S; i++) {
            int j = i - P;  // 처음 자리와 끝자리의 값만 뺴고 더해주며 문자열을 범위내에서 이동시킴.
            Add(A[i]);
            Remove(A[j]);
            if (checkSecret == 4) result++; // 각 이동 떄 마다 조건을 충족하는지 여부 확인해주기
        }
        System.out.println(result);
        bf.close();
    }
    // swich문을 사용해서 조건에 맞을 떄 마다 값을 추가하고 뺴주기
    private static void Remove(char c) {
        switch (c) {
            case 'A':
                if (myArr[0] == checkArr[0]) checkSecret--;
                myArr[0]--;
                break;
            case 'C':
                if (myArr[1] == checkArr[1]) checkSecret--;
                myArr[1]--;
                break;
            case 'G':
                if (myArr[2] == checkArr[2]) checkSecret--;
                myArr[2]--;
                break;
            case 'T':
                if (myArr[3] == checkArr[3]) checkSecret--;
                myArr[3]--;
                break;
        }
    }

    private static void Add(char c) {
        switch (c) {
            case 'A':
                myArr[0]++;
                if (myArr[0] == checkArr[0]) checkSecret++;
                break;
            case 'C':
                myArr[1]++;
                if (myArr[1] == checkArr[1]) checkSecret++;
                break;
            case 'G':
                myArr[2]++;
                if (myArr[2] == checkArr[2]) checkSecret++;
                break;
            case 'T':
                myArr[3]++;
                if (myArr[3] == checkArr[3]) checkSecret++;
                break;
        }
    }
}

<핵심 개념>

슬라이딩 윈도우의 경우 2개의 포인터로 범위를 지정하고, 범위를 유지하며 이동하는 개념이다.
데이터의 크기 확인 > 데이터가 백만이라 0(n)의 시간복잡도를 가진 슬라이딩윈도우의 개념을 활용한다.
문제의 포인트 중 하나가 문자열과 관련된 배열을 처리하는 방법이었는데 계속 파이썬만 풀다보니 딕셔너리만 생각났었다. 그래서 딕셔너리로 풀어야되나 고민했는데 문제에서 문자열의 순서와 상관 없이 슬라이딩 범위내에 들어온 문자 중 조건에 맞는 문자의 갯수만 충족하면 되서 그냥 배열에 넣었고 switch문을 통해 분기 처리해서 조건을 확인해주었다. (파이썬엔 switch가 없다.)

profile
두둥탁 뉴비등장

4개의 댓글

comment-user-thumbnail
2023년 7월 18일

아주 유익한 내용이네요!

1개의 답글
comment-user-thumbnail
2023년 7월 18일

잘 읽었습니다. 좋은 정보 감사드립니다.

1개의 답글