개발👩‍💻/알고리즘

백준 js 17299: 오등큰수

gigibean 2021. 5. 3. 21:25
728x90

문제

크기가 N인 수열 A = A1, A2, ..., AN이 있다. 수열의 각 원소 Ai에 대해서 오등큰수 NGF(i)를 구하려고 한다.

Ai가 수열 A에서 등장한 횟수를 F(Ai)라고 했을 때, Ai의 오등큰수는 오른쪽에 있으면서 수열 A에서 등장한 횟수가 F(Ai)보다 큰 수 중에서 가장 왼쪽에 있는 수를 의미한다. 그러한 수가 없는 경우에 오등큰수는 -1이다.

예를 들어, A = [1, 1, 2, 3, 4, 2, 1]인 경우 F(1) = 3, F(2) = 2, F(3) = 1, F(4) = 1이다. A1의 오른쪽에 있으면서 등장한 횟수가 3보다 큰 수는 없기 때문에, NGF(1) = -1이다. A3의 경우에는 A7이 오른쪽에 있으면서 F(A3=2) < F(A7=1) 이기 때문에, NGF(3) = 1이다. NGF(4) = 2, NGF(5) = 2, NGF(6) = 1 이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

출력

총 N개의 수 NGF(1), NGF(2), ..., NGF(N)을 공백으로 구분해 출력한다.

예제 입력 1 복사

7

1 1 2 3 4 2 1

예제 출력 1 복사

-1 -1 1 2 2 1 -1

 

접근

17298과 비슷하다.

다른 부분은 배열 input의 값에 해당하는 input[i]의 중복 count를 비교해야 한다.

예를 들어 input = [1, 1, 2, 3, 4, 2, 1]이라면,

중복 count(F(i)), F(0) = 3 이된다. 이 F를 배열로 표현하면

F=[3,3,2,1,1,2,3] 이 된다. 이 F에서 F[i]오른쪽에 있는 수 중 가장 큰 왼쪽에 있는 수를 구하면 된다.

 

그래서 처음에 문제를 풀 때는 각 input[i]에 중복 count를 구해서 F배열에 F[i]에 해당하는 중복 count를 넣었다.

let F = [];
for (let x of input) {
  let re = `${x}`;
  const reg = new RegExp(re, "g");
  F.push(strInput.match(reg).length);
}
console.log(F);

그리고 루프를 통해 F[i]와 F[stack.top()]을 비교해서 답을 구하려했는데,

메모리 초과가 떴다. 아마 F[] 배열이 새로 생성되는 과정에서 메모리가 초과된 듯해서 다시 고민을 했다.

 

메모리를 잡아먹는 배열을 최소화해서 풀기 위해서 F라는 배열을 따로 만들지 않고,

중복 없이 input의 각 값들을 key로, 중복 count를 value로  하는 count 객체를 하나 생성했다.

예) count = {1: 3, 2: 2, 3: 1, 4:1}

그리고 나서 똑같이 루프문 내에서 비교를 하는데, 따로 배열이 없으니 count의 key에 해당하는 input[i]의 value를 찾는 식으로 조건문을 바꿨다.

for (let i = 0; i < input.length; i++) {
  // input[i]에 대응하는 count value와 input[stack[stack.top()]]에 value
  // 1️⃣
  while (
    stack.length &&
    count[input[stack[stack.length - 1]]] < count[input[i]]
    // ①stack에 top에 있는 수를 인덱스로 갖는 input의 값을 key로 갖는 count의 value와 
    // ②input[i]를 key로 갖는 count의 value 를 비교
  ) {
    result[stack.pop()] = input[i];
  }
  // 2️⃣
  stack.push(i);
}

①과②에서 구하려는 count[input[stack[stack.top()]] || input[i]] 는 모두 앞서 구했던 F[stack[stack.top()] || F[i] 와 같다.

 

input = [1, 1, 2, 3, 4, 2, 1]

count = { '1': 3, '2': 2, '3': 1, '4': 1 }

result = [-1, -1, -1, -1, -1, -1, -1]

1️⃣과 2️⃣를 구하는 과정을 보면

i = 0 일 때:

1️⃣stack = [] 이므로 넘어간다

2️⃣stack = [0] 이 된다.

 

i = 1 일 때:

1️⃣stack = [0]

stack[stack.top] 은 0,

input[0]은 1,

count[1] = 3

①은 3, ②는 3 이므로 넘어간다.

2️⃣

stack = [0,1]이 된다.

 

i =  2일 때:

1️⃣stack = [0, 1]

stack.top = 1

① 은 3, ②는 2 이므로 넘어간다.

2️⃣

stack = [0, 1, 2] 된다.

 

i = 3 일 때:

1️⃣stack = [0, 1, 2]

stack.top = 2

① 은 2, ②는 1 이므로 넘어간다.

2️⃣

stack = [0, 1, 2, 3] 된다.

 

i = 4 일 때:

1️⃣stack = [0, 1, 2, 3]

stack.top = 3

① 은 1, ②는 1 이므로 넘어간다.

2️⃣

stack = [0, 1, 2, 3, 4] 된다.

 

i = 5 일 때:

1️⃣ stack = [0, 1, 2, 3, 4]

stack.top = 4

① 은 1, ②는 2 이므로

.result = [-1, -1, -1, -1, 2, -1, -1]가 되고

stack.pop() 된다.

 

stack.top = 3

① 은 1, ②는 2 이므로

.result = [-1, -1, -1, 2, 2, -1, -1]가 되고

stack.pop() 된다.

 

stack.top = 2

① 은 3, ②는 3 이므로 넘어간다.

2️⃣

stack = [0, 1, 2] 된다.

 

i = 6 일 때:

1️⃣stack = [0 ,1, 2, 5]

stack.top = 5

① 은 2, ②는 3 이므로

.result = [-1, -1, -1, 2, 2, 1, -1]가 되고

stack.pop() 된다.

 

stack.top = 2

① 은 2, ②는 3 이므로

.result = [-1, -1, 1, 2, 2, 1, -1]가 되고

stack.pop() 된다.

 

stack.top = 1

① 은 2, ②는 2 이므로 넘어간다.

2️⃣

stack = [0, 1] 된다.

 

i = 7 일 때:

넘어간다.

stack = [0, 1, 7] 이 된다.

 

전체 코드

시도

1️⃣F배열 만들어서 돌리기 -> 시간 초과

// match용 str
const strInput = input.join("");

// F[] = F(Ai) = Ai의 중복개수,
// index = i,
// input[i] = A
let F = [];
for (let x of input) {
  let re = ;
  const reg = new RegExp(re, "g");
  F.push(strInput.match(reg).length);
}

let stack = [];
for (let i = 0; i < F.length; i++) {
  // F[stack.top()]보다 큰 F[i]를 찾아
  // result[i]에 input[i] 넣기
  while (stack.length && F[stack[stack.length - 1]] < F[i]) {
    result[stack.pop()] = input[i];
  }
  stack.push(i);
}

console.log(result.join(" "));
반응형