
{A, C, G, T} 로 이루어진 길이 S 의 문자열이 주어진다.
해당 문자열에서 P 의 길이를 가진 부분 문자열을 뽑아서 비밀 번호를 만들려고 한다.
비밀 번호를 만들기 위한 조건으로 {A, C, G, T} 가 각각 몇개 이상으로 반드시 있어야하는지 배열로 주어진다.
예를 들어 [2, 0, 0, 1] 이 주어지면 A 2개 이상, T 1개 이상이 포함되야 한다는 뜻이다.
이렇게 하여 만들 수 있는 비밀 번호의 수를 출력하라
이 문제는 Sliding Window 알고리즘을 이용해야 한다.
해당 알고리즘을 간략하게 설명하면 다음과 같다.
1, 2, 3, 4, 5, 6 이 중에 3개의 연속된 숫자를 더하는 최대값을 찾을 때,
(1, 2, 3), 4, 5, 6 다음과 같이 먼저 계산을 한다. 1 + 2 + 3 = 66 - 1 + 4 를 진행한다. 그럼 이 값은 1, (2, 3, 4), 5, 6 이 된다.즉 간단히 표현하면 삭제 - 유지할 부분 - 추가 이렇게 반복하는 것이고
이렇게 계산하는 방식을 Sliding Window 알고리즘이라고 한다.
그럼 Sliding Window 알고리즘을 이용하여 로직을 생각해보자
0 부터 P 까지 문자열을 확인하여 해당 문자열에 {A, C, G, T} 가 각각 몇개 인지 숫자를 센다.answer 변수에 +1{A, C, G, T} 가 각각 몇개 인지 이전 값에서 삭제되는 값과 추가할 값을 수정하여 갱신answer 변수에 + 1전체 풀이
let fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
let [N, M] = input.shift().split(' ').map(Number);
const PASSWORD = input.shift();
const REQUIRE = input.shift().split(' ').map(Number);
// ACGT 가 몇개 있는지 저장해줄 변수
let passwordCondition = {};
passwordCondition['A'] = 0;
passwordCondition['C'] = 0;
passwordCondition['G'] = 0;
passwordCondition['T'] = 0;
// 정답 갯수
let answer = 0;
// 조건에 충족하는지 확인
const CHECK = () => {
if (passwordCondition['A'] >= REQUIRE[0] && passwordCondition['C'] >= REQUIRE[1] && passwordCondition['G'] >= REQUIRE[2] && passwordCondition['T'] >= REQUIRE[3]) {
return true;
}else return false;
};
// 첫번째 문자열을 돌며 ACGT 가 각각 몇개 있는지 갱신
for (let i = 0; i < M; i++) {
passwordCondition[PASSWORD[i]] += 1;
}
// 만약 첫번째 문자열이 조건을 충족하는지 확인
answer = CHECK() ? answer + 1 : answer;
// 첫번째 문자열 다음부터 Sliding Window 알고리즘을 이용하여 갯수를 새줌
for (let i = 0; i < PASSWORD.length - M; i++) {
passwordCondition[PASSWORD[i]] -= 1;
passwordCondition[PASSWORD[i + M]] += 1;
answer = CHECK() ? answer + 1 : answer;
}
console.log(answer);
최근에 문자열 문제를 풀면서 처음 들어보는 알고리즘을 많이 보는 것 같다.
이 Sliding Window 알고리즘은 투 포인터와 비슷한 것 같지만, 투 포인터의 경우 크기가 유동적일 경우 더 유용할 것 같고 크기가 일정한 문제의 경우 Sliding Window 알고리즘이 더 유용할 것 같다.