21. Sales event

해시 테이블을 활용한 데이터 카운팅

algorithm test

21. sales event

Sunhyup Lee. (2024). To be a successful candidate for the coding test. goldenrabbit. p.251.

Problem solving

문제 이해

1. 입력값 설명

  • want: 정현이가 원하는 제품의 이름이 담긴 배열 (예: [‘바나나’, ‘사과’, ‘쌀’])
  • number: 각 제품별로 정현이가 원하는 수량(예: [3, 2, 1])
  • discount: 할인중인 제품 리스트가 날짜별로 순서대로 담겨 있음(예: [‘치킨, ‘사과, ‘바나나’…])


2. 문제 조건

  • XYZ 마트에서 매일 1가지 제품이 할인됩니다.
  • 할인 상품베열(discount)에서 정현이가 원하는 모든 제품과 수량이 정확히 일치하는 10일 연속 구간을 찾아야 합니다.
  • 정현이가 원하는 제품과 수량이 일치하지 않으면 회원가입을 하지 않으며, 가능한 날이 없으면 0을 반환합니다.


3. 목표

  • 할인 목록에서 10일 연속 구간을 순회하며, 해당 구간의 할인 상품들이 want, number조건을 만족하는 지를 확인합니다.
  • 조건에 맞는 구간이 몇 개 있는지 반환합니다.


4. 풀이요약

  • 할인 목록(discount)의 모든 10일 구간을 순회
  • 각 구간에서 제품과 수량이 wantnumber 조건을 만족하는지 검사
  • 조건을 만족하면 카운트 증가
  • 모든 구간을 확인한 뒤 카운트를 반환
function solution(want, number, discount) {
  // 1. 원하는 제품과 수량을 딕셔너리 형태로 저장
  const wantMap = {};
  for (let i = 0; i < want.length; i++) {
    wantMap[want[i]] = number[i];
  }

  // 2. 슬라이딩 윈도우 크기와 결과 변수 초기화
  const windowSize = 10;
  let count = 0;

  // 3. 현재 윈도우의 제품 개수를 저장할 딕셔너리
  const currentWindow = {};

  // 4. 초기 윈도우 설정 (첫 번째 10일)
  for (let i = 0; i < windowSize; i++) {
    const product = discount[i];
    currentWindow[product] = (currentWindow[product] || 0) + 1;
  }

  // 5. 맵 비교 함수 (wantMap과 currentWindow가 같은지 확인)
  const isMatch = (map1, map2) => {
    for (const key in map1) {
      if (map1[key] !== map2[key]) return false;
    }
    return true;
  };

  // 6. 첫 번째 윈도우 검사
  if (isMatch(currentWindow, wantMap)) {
    count++;
  }

  // 7. 슬라이딩 윈도우 이동 (한 칸씩 옮기면서 검사)
  for (let i = windowSize; i < discount.length; i++) {
    // 윈도우에서 빠지는 값 제거
    const outgoing = discount[i - windowSize];
    currentWindow[outgoing]--;
    if (currentWindow[outgoing] === 0) {
      delete currentWindow[outgoing];
    }

    // 윈도우에 새로 들어오는 값 추가
    const incoming = discount[i];
    currentWindow[incoming] = (currentWindow[incoming] || 0) + 1;

    // 현재 윈도우가 조건을 만족하는지 확인
    if (isMatch(currentWindow, wantMap)) {
      count++;
    }
  }

  return count;
}



1단계: 원하는 제품과 수량을 딕셔너리에 저장

wnatnumber 배열을 사용해 정현이가 원하는 제품과 개수를 딕셔너리 형태로 만든다.

const want = ["banana", "apple", "rice", "pork"];
const number = [3, 2, 2, 1];

이를 딕셔너리로 저장하면

const wantMap = {
  banana: 3,
  apple: 2,
  rice: 2,
  pork: 1,
};

이제 wantMap은 정현이가 원하는 조건을 담고 있습니다.


2단계: 슬라이딩 윈도우 설정

  • 슬라이딩 윈도우 크기는 문제에서 주어진 “연속된 10일”을 의미합니다. 따라서, windowSize = 10으로 설정합니다.

  • count 변수는 조건을 충족하는 연속된 10일의 횟수를 셀 때 사용합니다. 처음에는 count = 0으로 초기화 합니다.


3단계: 첫번째 10일의 제품 개수 계산

  • 할인 목록인 discount배열의 첫 번째 10일을 기준으로, 각 제품의 개수를 세어 저장합니다.
  • 이 때, currentWindow라는 딕셔너리를 사용해 현재 10일 동안의 제품 개수를 추적합니다.

예를 들어, 다음과 같은 discount배열이 있다고 가정합시다.

const discount = [
  "chicken",
  "apple",
  "apple",
  "banana",
  "rice",
  "pork",
  "banana",
  "banana",
  "banana",
  "apple",
];

여기서 첫번째 10일을 계산하면

currentWindow = {
  chicken: 1,
  apple: 3,
  banana: 4,
  rice: 1,
  pork: 1,
};

이 작업은 루프를 돌며 각 제품의 개수를 세어서 currentWindow에 저장합니다.


4단계: 두 딕셔너리 비교 함수

  • wantMapcurrentWindow가 같은지 확인하기 위해, isMatch라는 함수를 정의합니다.
const isMatch = (map1, map2) => {
  for (const key in map1) {
    if (map1[key] !== map2[key]) return false;
  }
  return true; // 조건 충족
};
  1. map1(wantMap)의 모든 키를 확인하며,
  2. map2(currentWindow)와 각 값이 동일한지 비교합니다.
  3. 값이 하나라도 다르면 false, 모두 같은면 true를 반환합니다.


5단계: 첫번째 윈도우 검사

  • 첫 번째 10일(currentWindow)이 정현이의 조건(wantMap)을 만족하는지 확인합니다.
if (isMatch(currentWindow, wantMap)) {
  count++;
}

만약 첫번째 윈도우가 조건을 만족하면, count를 1 증가시킵니다.


6단계: 슬라이딩 윈도우

이제 discount 배열에서 윈도우를 한 칸 씩 옮기면서 조건을 확인합니다.

(1) 윈도우에서 빠지는 값 제거

  • 윈도우가 한 칸 이동하면, 맨 앞의 값(윈도우에서 나가는 값)을 제거합니다.
const outgoing = discount[i - windowSize];
currentWindow[outgoing]--;
if (currentWindow[outgoing] === 0) {
  delete currentWindow[outgoing]; // 개수가 0이면 삭제
}

(2) 새로 들어오는 값 추가

  • 윈도우의 오른쪽으로 새로 들어오는 값을 추가합니다.
const incoming = discount[i];
currentWindow[incoming] = (currentWindow[incoming] || 0) + 1;

(3) 조건 확인

  • 새로 갱신된 currentWindow가 조건(wantMap)을 만족하는지 확인합니다.
if (isMatch(currentWindow, wantMap)) {
  count++;
}


7단계: 최종 결과 반환

  • 조건을 충족한 구간의 개수(count)를 반환합니다.