
방법은 아래와 같음!
1. 두 포인터(인덱스)를 시작점으로 초기화.
2. 규칙에 따라 포인터 이동
3. 두 포인터 중 하나 혹은 둘 모두 끝에 도달할 때까지 반복.
예제로 알아보자!

위 문제를 보고 가장 먼저 떠올릴 수 있는 방법은 Brute Force임.
하지만 아래의 두 경우는 시간초과를 받을 수 있음.
int ans = 0;
for (int i = 0; i < N; i++) {
for (int j = i: j <N; j++) {
int sum = 0;
for (int k = i; k <= j; k++)
sum += arr[j];
if (sum == M) ans++;
else if (sum > M) break;
}
}
int ans = 0;
for (int i = 0; i < N; i++) {
int sum = 0;
for (int j= i:j < N; j+) {
sum += arr [j];
if (sum == M) ans++;
if (sum >= M) break;
}
}
문제에서는 자연수 수열이므로, 각 시작점 i에 대해 [ i : j ]가 M이 될 수 있는지
Binary Search + 누적합으로도 해결할 수 있음.
O(N * logN)// 미리 누적합을 구한 후
long[] acc = new long[N + 1];
for (int i = 1; i < N; i++)
acc[i] = acc[i - 1] + arr[i];
// 이분탐색을 이용.
int ans = 0;
for (int i = 1; i < N; i++) {
int l = i, r = N;
while (l < r) {
int m = (l+ r) / 2;
long sum = acc[m] - acc[i - 1];
if (sum > M)r = m - 1;
else if (sum < M) l = m + 1;
else {
ans++;
break;
}
}
}
단 음수를 포함한 정수 수열일 경우 위 방법은 통하지 않음.
위 방법도 가능하지만 이제 Two Pointer를 이용한 방법을 보여주도록 하겠음.

[ i : j ] 의 j가 늘어나면 구간의 합이 증가한다는 특성을 이용해 [ i : j ] == M을 만족하는 구간을
Two Pointer로 찾아보는 것임.
만약 두 인덱스를 7과 8이라고 가정해보자.
[ i : j ]의 연속합 ⇒ $a_7$ + $a_8$ = 4 이므로 원하는 값인 5에 만족하지 못함.
이때 i는 고정한 후, j를 오른쪽으로 이동하여 포함하는 범위를 늘림.
[ i : j ]의 연속합 ⇒ $a_7$ + $a_8$ + $a_9$ = 5 으로 값은 만족하며,
i = 7 인 구간의 유일한 답이 됨.
j가 증가하면 합이 늘어나므로 더 이상 확인할 필요가 없음.
이후 i = 8 인 경우를 조사할 때, Two Pointer는 아래와 같은 방법을 사용.
i = 7에 대해 [ 7 : 7 ], [ 7 : 8 ] 이 원하는 값(5) 보다 작았음을 이용하여,
i = 8일때 역시 [ 8 : 9 ] 부터 조사를 시작할 수 있음.
([ 8 : 8 ] 구간을 건너 뜀.)
Two Pointer에서 중요한 것은 이전 탐색 포인터를 재사용하는 것!
Two Pointer를 이용한 풀이
시간복잡도 O(N)
i가 0부터 N - 1까지 움직이는 동안 rightIndex의 총 움직임이 N이므로
전체 시간복잡도는 O(N)이 나온다.
int rightIndex = 0;
int currentSum = arr [0];
for (int i = 0; i < N; i++) {
while (currentSum < M && rightIndex < N - 1)
currentSum += arr[++rightIndex];
if (currentSum == M) ans++;
currentSum -= arr[i];
}
https://www.acmicpc.net/problem/2470

이 문제는 BinarySearch로 해결했지만, Two Pointer를 이용해서도 풀 수 있음.
각 i에 대해 j를 이동하면서 최적의 쌍이 되는 값을 찾는데 이때,
i, j 의 이동 방향은 마주보는 방향이 됨.
해당 문제는 서로 다른 두 용액을 혼합하는 문제이므로, i == j 가 되면 중단.
O(N)leftIndex를 고정하여 검사하는 방법int rightIndex = N - 1;
for (int i = 0; i < rightIndex; i++) {
int currentSum = arr[i] + arr[rightIndex];
int currentAbs = Math.abs(currentSum);
if (ansAbs › currentAbs) {
ansAbs = currentAbs;
ansLeftIndex = i;
ansRightIndex = rightIndex;
}
while (currentSum > 0 && i < rightIndex - 1) {
currentSum = arr[i] + arr[--rightIndex];
currentAbs = Math.abs(currentSum);
if (ansAbs > currentAbs) {
ansAbs = currentAbs;
ansLeftIndex = i;
ansRightIndex = rightIndex;
}
}
}
leftIndex, rightIndex를 모두 움직이는 방법int leftIndex = 0;
int rightIndex = N - 1;
while (leftIndex ‹ rightIndex) {
int currentSum = arr[leftIndex] + arr[rightIndex];
int currentAbs = Math.abs(currentSum);
if (ansAbs > currentAbs) {
ansAbs = currentAbs;
ansLeftIndex = leftIndex;
ansRightIndex = rightIndex;
}
if (currentSum > 0) rightIndex--;
else leftIndex++;
}
https://www.acmicpc.net/problem/2003
O(N)public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
int[] arr = new int[N];
st = new StringTokenizer(br.readLine());
for(int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
int ans = 0;
int rightIndex = 0;
int currentSum = arr[0];
// 하나의 포인터는 고정 사용
for (int i= 0; i < N; i ++) {
while(currentSum < M && rightIndex < N - 1)
currentSum += arr[++rightIndex];
if (currentSum == M) ans++;
currentSum -= arr[i];
}
System.out.println(ans);
}
}
int left = 0, right = 0, sum = 0;
while (true) {
if (sum >= M) {
sum -= arr[left++];
} else if (right == N) {
break;
} else {
sum += arr[right++];
}
if (sum == M) {
ans++;
}
}