PS/Algorithm
[Algorithm] 이중 포문 조정 - 구간합
sebinChu
2024. 4. 17. 18:30
개요
시간복잡도 줄이는 법을 학습하면서 해본 정리
코드트리를 참고하였다!
어떤 배열에서 특정 합을 만족하며 가장 크기가 큰 구간의 크기를 찾기 위해 완전탐색을 하면 다음과 같다.
완전탐색 - O(N**3)
# 이 배열에서 특정 구간을 골라서,
# 합이 10이 넘지 않아야 하고,
# 배열의 크기가 가장 커야 한다. 이때의 배열 크기는?
arr = [0,6,3,2,4,9,1]
n = len(arr)
target=10
# 무식하게 구해보기
for i in range(1, n+1):
for j in range(i, n+1):
total = 0
for k in range(i, j+1):
total += arr[k]
if total <= target:
ans = max(ans, j-i+1)
이 코드의 복잡도는 O(N**3)이다.
복잡도를 줄여보자.
구간 내 시작점과 끝 활용 - O(N**2)
# 일단 모든 구간 탐색
for i in range(1,n+1):
total=0
for j in range(i,n+1):
total += arr[j]
# 특정합이 타겟합을 넘었을 때
if total > target : break
ans = max(ans, j-i+1)
*이 경우, j는 i 값이 달라질 때마다 i에서 새로 시작하므로 후퇴하는 경우가 많이 발생하게 된다. ➡️ 시간복잡도는 O(N**2)
# while로 표현해보면
total=0
j=0
for i in range(1,n+1):
while j+1 <= n and total + arr[j+1] <= target:
total += arr[j+1]
j += 1
ans = max(ans, j-i+1)
# 다음 구간으로 넘어가기 전에 arr[i]의 값은 해당 구간에서 제외한다.
total -= arr[i]
투 포인터는 이렇게 i, j가 한 방향으로 전진하면서 연산을 수행하는 형태를 갖는다. ➡️ 시간복잡도는 O(N)
이 i, j 전진 테크닉은 실제로 이중 포문으로 작성되어 있지만, i와 j가 한 방향으로 진행되기 때문에 시간 복잡도는 두 for loop의 곱이 아니라 합으로 나타내진다.
구간 내 특정 합이 존재하고, 중복되는 값 Counting
같은 숫자가 3개 이상 있지 않은 경우 중, 가장 큰 구간의 크기
### 같은 숫자가 3개 이상 있지 않은 경우 중, 가장 큰 구간의 크기
n,s=map(int,input().split())
arr=[0] + list(map(int,input().split()))
# 각 숫자마다 몇 번씩 나왔는지 카운팅하는 배열을 추가적으로 만들어 관리한다.
## cnt_arr의 인덱스는 숫자 정보, value는 해당 숫자가 등장한 개수
cnt_arr=[0]*4
ans=0
j=0
for i in range(1,n+1):
while j+1 <= n and cnt_arr[arr[j+1]] != 2:
cnt_arr[arr[j+1]]+=1
j+=1
ans=max(ans,j-i+1)
# 하나 제거
cnt_arr[arr[i]] -= 1