SlidingWindow
SlidingWindow
슬라이딩 윈도우 알고리즘
고정 크기의 윈도우가 배열이나 연속적인 데이터 구조를 따라 이동하면서 문제를 해결하는 알고리즘 기법입니다.
이 알고리즘은 배열 또는 연속적인 데이터 구조에서 연속된 부분집합(윈도우)을 처리할 때 유용하게 사용되는 알고리즘 기법입니다.
주로 부분집합의 합, 최대값, 최소값 등을 구하는 문제에 적용됩니다.
또한 슬라이딩 윈도우 알고리즘은, 2개의 포인터로 범위를 지정한 다음, 범위를 유지한 채로 이동하며 문제를 해결하는 방식이어서 투 포인터 알고리즘과 방식이 매우 비슷하고 원리도 간단합니다.
주요 아이디어
- 초기에 윈도우를 설정하고, 윈도우의 크기를 고정합니다.
- 윈도우를 한번에 한 칸씩 이동시키며 새로운 요소를 추가하고 이전 요소를 제거합니다.
- 새로운 요소를 추가한 후에는 윈도우 내의 데이터를 사용하여 필요한 계산을 수행합니다.
- 윈도우를 끝까지 이동시키면서 필요한 계산을 반복하고 결과값을 얻습니다.
특징
데이터를 한번만 처리하면서 원하는 결과를 얻을 수 있습니다.
이로 인해 효율적인 시간 복잡도를 가질 수 있습니다.
슬라이딩 윈도우 알고리즘은 연속적인 데이터 구조를 처리하는 많은 문제에 적용될 수 있으며, 특히 데이터의 크기가 크거나, 최적화가 필요한 경우 유용하게 사용됩니다.
종류
- 고정 크기의 슬라이딩 윈도우
- 가변 크기의 슬라이딩 윈도우
- 최소값이나 최대값을 구하는 슬라이딩 윈도우
- 부분집합의 합을 구하는 슬라이딩 윈도우
장점
- 시간 복잡도 개선: 슬라이딩 윈도우 알고리즘은 데이터를 한 번만 처리하면서 원하는 결과를 얻을 수 있습니다. 따라서 데이터를 다시 처리하지 않아도 되므로 시간 복잡도를 개선할 수 있습니다.
- 메모리 효율성: 슬라이딩 윈도우 알고리즘은 고정된 크기의 윈도우를 사용하여 문제를 해결하므로 필요한 메모리의 양이 고정되어 있습니다. 이는 메모리 효율성을 높이는 데 도움이 됩니다.
- 쉬운 구현: 슬라이딩 윈도우 알고리즘은 간단한 아이디어를 기반으로 하고 있으며, 주어진 문제에 대한 직관적인 해결 방법을 제공합니다. 이로 인해 상대적으로 쉽게 구현할 수 있습니다.
활용 상황
- 부분집합의 합: 주어진 배열에서 특정 합계를 갖는 부분집합을 찾는 문제에 슬라이딩 윈도우 알고리즘을 적용할 수 있습니다.
- 최대값 또는 최소값 구하기: 배열이나 리스트에서 연속된 부분집합 중에서 최대값 또는 최소값을 찾는 문제에 슬라이딩 윈도우 알고리즘을 사용할 수 있습니다.
- 특정 개수 이상의 문자열 패턴 찾기: 문자열에서 특정 문자 또는 문자열 패턴이 특정 개수 이상 등장하는 부분문자열을 찾는 문제에 슬라이딩 윈도우 알고리즘을 적용할 수 있습니다.
- 특정 조건을 만족하는 부분집합 찾기: 배열 또는 리스트에서 특정 조건을 만족하는 연속된 부분집합을 찾는 문제에 슬라이딩 윈도우 알고리즘을 사용할 수 있습니다.
- 데이터의 윈도우 기반 분석: 데이터 스트림에서 일정한 윈도우를 유지하면서 통계 또는 분석을 수행하는 데 슬라이딩 윈도우 알고리즘을 활용할 수 있습니다.
코드 예제 [ 백준 12891번 문제 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package Silver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(in.readLine());
int A = Integer.parseInt(st.nextToken());
int B = Integer.parseInt(st.nextToken());
String[] dataList = new String[A];
int[] numberList = new int[4];
int[] check = new int[4];
String cc = in.readLine();
int result = 0;
for (int i = 0; i < A; i++) {
String tt = String.valueOf(cc.charAt(i));
dataList[i] = tt;
}
st = new StringTokenizer(in.readLine());
for (int z = 0; z < 4; z++) {
numberList[z] = Integer.parseInt(st.nextToken());
}
for (int i = 0; i < B; i++) {
switch (dataList[i]) {
case "A":
check[0]++;
break;
case "C":
check[1]++;
break;
case "G":
check[2]++;
break;
case "T":
check[3]++;
break;
}
}
if (numberList[0] <= check[0] &&
numberList[1] <= check[1] &&
numberList[2] <= check[2] &&
numberList[3] <= check[3]) {
result++;
}
for (int c = B; c < A; c++) {
switch (dataList[c - B]) {
case "A":
check[0]--;
break;
case "C":
check[1]--;
break;
case "G":
check[2]--;
break;
case "T":
check[3]--;
break;
}
switch (dataList[c]) {
case "A":
check[0]++;
break;
case "C":
check[1]++;
break;
case "G":
check[2]++;
break;
case "T":
check[3]++;
break;
}
if (numberList[0] <= check[0] &&
numberList[1] <= check[1] &&
numberList[2] <= check[2] &&
numberList[3] <= check[3]) {
result++;
}
}
System.out.println(result);
}
}
This post is licensed under CC BY 4.0 by the author.