Programmers
[프로그래머스 Level.3] 스타 수열 (월간 코드 챌린지 시즌1 ) (Java)
Devtraces
2023. 4. 12. 14:06
문제 링크
https://school.programmers.co.kr/learn/courses/30/lessons/70130
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
코딩테스트 연습 > 월간 코드 챌린지 시즌1 > 스타 수열
문제 설명
다음과 같은 것들을 정의합니다.
- 어떤 수열 x의 부분 수열(Subsequence)이란, x의 몇몇 원소들을 제거하거나 그러지 않고 남은 원소들이 원래 순서를 유지하여 얻을 수 있는 새로운 수열을 말합니다.
- 예를 들어, [1,3]은 [1,2,3,4,5]의 부분수열입니다. 원래 수열에서 2, 4, 5를 제거해서 얻을 수 있기 때문입니다.
- 다음과 같은 조건을 모두 만족하는 수열 x를 스타 수열이라고 정의합니다.
- x의 길이가 2 이상의 짝수입니다. (빈 수열은 허용되지 않습니다.)
- x의 길이를 2n이라 할 때, 다음과 같은 n개의 집합 {x[0], x[1]}, {x[2], x[3]}, ..., {x[2n-2], x[2n-1]} 의 교집합의 원소의 개수가 1 이상입니다.
- x[0] != x[1], x[2] != x[3], ..., x[2n-2] != x[2n-1] 입니다.
- 예를 들어, [1,2,1,3,4,1,1,3]은 스타 수열입니다. {1,2}, {1,3}, {4,1}, {1,3} 의 교집합은 {1} 이고, 각 집합 내의 숫자들이 서로 다르기 때문입니다.
1차원 정수 배열 a가 매개변수로 주어집니다. a의 모든 부분 수열 중에서 가장 길이가 긴 스타 수열의 길이를 return 하도록 solution 함수를 완성해주세요. 이때, a의 모든 부분 수열 중에서 스타 수열이 없다면, 0을 return 해주세요.
제한사항
- a의 길이는 1 이상 500,000 이하입니다.
- a의 모든 수는 0 이상 (a의 길이) 미만입니다.
입출력 예
a | result |
[0] | 0 |
[5,2,3,3,5,3] | 4 |
[0,3,3,0,7,2,0,2,2,0] | 8 |
입출력 예 설명
입출력 예 #1
- a의 부분 수열 중에서 주어진 조건을 모두 만족하는 스타 수열이 없으므로, 0을 return 해야 합니다.
입출력 예 #2
- [5,2,5,3], [5,3,3,5] 는 a의 부분 수열인 동시에 스타 수열입니다. a의 부분 수열 중 이보다 더 긴 스타 수열은 없으므로, 4를 return 해야 합니다.
입출력 예 #3
- [0,3,3,0,7,0,2,0] 는 a의 부분 수열인 동시에 스타 수열입니다. a의 부분 수열 중 이보다 더 긴 스타 수열은 없으므로, 8을 return 해야 합니다.
나의 코드
import java.util.*;
class Solution {
public int solution(int[] a) {
int answer = 0;
int[] cnt = new int[a.length];
for(int i=0; i<a.length; i++) cnt[a[i]]++;
for(int i=0; i<cnt.length; i++) {
if(cnt[i] == 0) continue; // 원소 개수가 없으면 건너뜀
if(cnt[i] <= answer) continue; // 원소 개수가 이미 다른 교집합 원소가 사용된 수 * 2 이하이면 건너뜀
// (교집합 원소 사용된 횟수 * 2 가 최대 길이이기 때문에 answer보다 크게 나올 수 없음)
int count = 0; // 해당 교집합 원소가 사용된 횟수
for(int j=0; j<a.length-1; j++) {
if(a[j] != i && a[j+1] != i) continue; // 교집합 원소의 개수는 1 이상
if(a[j] == a[j+1]) continue; // 바로 다음 수와 동일하면 안된다. (x[0] != x[1], x[2] != x[3])
count++;
j++;
}
if(count > 1) answer = Math.max(answer, count); // 1번만 사용되면 스타수열 길이가 2로 교집합을 갖는 두 집합이 안생김
}
return answer * 2; // 사용된 횟수마다 (j, j+1) 두 개씩의 원소를 가짐
}
}
풀이
- 교집합으로 삼을 원소를 구하기 위해 cnt 배열을 만들어 a 배열을 순회하며 각 원소의 개수를 해당 수에 해당하는 인덱스에 저장한다.
- 이제 cnt 배열을 순회하며 해당 원소의 개수를 확인한 뒤 교집합으로 삼아 스타수열을 만든다.
- 우선 해당 원소를 교집합으로 삼을 것이기 때문에 개수가 1개 이하라면 넘어간다.
- 또한, 해당 원소의 개수 * 2가 answer 이하라면 해당 원소를 모두 사용하여 스타수열을 만들어도 길이가 answer와 같거나 작다는 것이므로 넘어간다.
- 둘 다 아니라면 해당 원소로 스타수열의 길이를 구하도록 한다.
- 스타수열을 만들면서 해당 원소가 사용된 횟수를 구하기 위해 int형 변수 count를 0으로 초기화한다.
- 이제 a배열을 다시 순회하면서 문제에 주어진 조건대로 교집합 원소는 무조건 포함해야 하는 것과 한 집합에 속하는 두 수가 같으면 안되는 것을 확인하여 둘 다 아니라면 교집합으로 삼을 원소를 증가시키고 a[j]와 a[j+1] 두 수를 스타수열에 포함하였으니 j를 2증가시켜 다음 수를 확인하도록 한다. 조건에 걸린다면 j를 1만 증가시켜 다음 수를 확인하면 된다.
- 이렇게 교집합 원소가 사용된 횟수를 구하여 사용된 횟수가 2 이상이라면 교집합 원소와 다른 수(j와 j+1)가 쌍을 이루므로 *2를 하여 answer와 비교하여 더 크다면 answer에 저장한다. (교집합 원소가 한 번만 사용되었다면 해당 원소를 교집합으로 각각 갖는 집합 두 개를 만들 수가 없다)