확신의 말을 만드는 법

1단계 : 목적의 시각화

확신이 주는 말을 종이에 옮겨 적는다.

이상적인 삶이 정확히 어떤 모습인지 분명하게 표현해야 한다.

인생에서 가장 중점적으로 개선해야 할 부분에 대한 확신의 말을 만들어야한다.

(건강에 관한 것이든, 사고방식에 관한 것이든, 인간관계에 대한 것이든)

확신의 말은 각 부분에서 정말로 원하는 상태를 분명히 하고 종이에 적는 것으로 시작한다.

 

2단계 : 질문 찾기

'왜'라는 질문이 당신을 움직이게 한다. 마음 가장 깊은 곳의 '왜'를 향해 끝없이 파고들어야 한다. '왜'를 발견하면 삶의 목표를 향해 돌진하게 될 것이다.

3단계 : 존재의 정의

내 인생은 내가 나아진 뒤에야 나아질 수 있다. 나를 개선하기 위해 수많은 시간을 투자한 후에야 외부 세계의 개선을 기대할 수 있다. 되어야만 하는 존재가 되고 해야만 하는 것을 할 때 살아보고 싶은 삶을 살 수 있다.

4단계 : 작은 시작

비전을 현실로 만들기 위해서는 일관된 노력이 필요하다.

빈도(얼마나 자주)와 양(얼마나 많이)과 정확한 시간(언제 시작해서 언제 끝낼 것인지)을 포함시키도록 하자

감당할 수 있는 보폭을 선택하자.

작은 목표를 이뤄가는 과정에서 느낄 수 있는 성취감을 쌓아라.

5단계 : 영감을 주는 명언

"영향력 있는 사람의 첫 번째 기술은 상대방이 자신을 세상에서 가장 중요한 사람처럼 느끼도록 진심으로 노력하는 것"

"나 역시도 내가 만난 모든 사람들을 이렇게 대할 것이다"라는 확신의 말을 만든다.

영감을 주는 문장을 읽었다면 나만의 확신의 말을 만들 때 활용하자. 매일 반복적으로 확신의 말을 외치면 문장 안에 담긴 철학을 저절로 흡수하게 된다.

 

확신의 말이 효과를 보려면 그것을 읽는 동안 감정을 이입하는 것이 좋다. 진정어린 감정을 불러일으켜 확신의 말마다 그 감정을 강력하게 불어넣어야 한다. 그 자체를 즐기자
당당한 자세로 서서 확신의 말을 암송한다거나, 깊은 호흡을 하고 주먹을 꽉 쥐거나 운동을 하며 확신의 말을 외친다. 신체의 움직임과 확신의 말을 결합한다.
삶에 포함시키고 싶은 새로운 목표, 꿈, 습관, 철학이 생긴다면 확신의 말에 첨가해야한다. 매일 일관되게 확신의 말을 읽어야 한다. 반드시 매일 읽어야 한다. 확신의 말을 하루의 일과로 박아놓야아한다.

 

직관의 시각화 : 확신과 다짐의 말을 구체화하는 시간

삶에서 일어날 구체적인 행위와 결과를 머릿속에 그림으로 그려보는 것이다. 성취하고자 하는 목표를 구체적으로 그린 다음, 목표를 달성하는 과정을 머릿속에서 예행연습하는 것이다.

 

확신과 다짐의 말을 읽고, 5분간 이상적인 하루를 살아가는 자신감 넘치고 즐겁게 일하는 나의 모습을 머릿속에 그려본다. 확신과 다짐의 말을 읽은 직후 그에 일치하는 삶을 사는 모습을 그리는 데 가장 쉽다.

 

원하는 것이 무엇이든 가질 수 있고 할 수 있으며 될 수 있다면 무엇을 가질 것인가? 무엇을 할 것인가? 무엇이 될 것인가? 내 인생을 완전히 변화시킬만한 원대한 목표, 간절한 바람, 설레는 꿈을 그려본다.

 

핵심은 성취하고자 했던 성공을 성취하는 모습을 그려보는 것이고, 끝내 성취했을 때의 끝내주는 기분을 미리 경험하는 것이다.

 

원하는 그림을 완성했다면 이제 그 그림과 완전히 일치하는 삶을 사는 나의 모습을 그려본다. 성공하는 삶을 만들기 위해 매일 해야하는 행동들에 매진하고 있는 모습을 그려본다. 그 과정을 즐긴다.

문제

로또 번호를 담은 배열 lottos, 당첨 번호를 담은 배열 win_nums가 매개변수로 주어집니다. 이때, 당첨 가능한 최고 순위와 최저 순위를 차례대로 배열에 담아서 return 하도록 solution 함수를 완성해주세요.

 

 

코드

const solution = (lottos, win_nums) => {
  let answer = [];

  let zeroCount = lottos.filter((el) => el === 0).length;
  let sameNums = lottos.filter((el) => win_nums.includes(el)).length;

  let min = 7 - sameNums >= 6 ? 6 : 7 - sameNums; // 최저순위
  let max = min - zeroCount < 1 ? 1 : min - zeroCount// 최고순위

  answer = [max, min];
  return answer;
}

const lottos = [44, 1, 0, 0, 31, 25];
const win_nums = [31, 10, 45, 1, 6, 19];
console.log(solution(lottos, win_nums));

문제

원하는 상품을 골라 가격과 배송비를 제출하라

현재 예산으로 최대 몇 명의 학생에서 선물을 사줄 수 있는지

상품 하나를 50% 할인해서 살 수 있는 쿠폰 있다. 배송비는 할인에 포함되지 않는다.

 

입력예제 |

예산 28

제출한 가격과 배송비 [6, 6], [2, 2], [4, 3], [4, 5], [10, 3]

출력예제 |

4

 

풀이

총 비용을 기준으로 정렬을 한다.

쿠폰을 어떤 가격에 적용시켜야하는가 -> 기준이 없기 때문에 모든 경우를 다 찾아봐야한다. 

 

첫 번째 상품을 할인받는다 생각하고, 총 비용에서 제외하고 계산

두 번째 상품을 할인받는다 생각하고 총 비용에서 제외하고 계산

.

.

.

다 해보는 것이 완전탐색

 

a[0]+a[1]  상품가격+배송비

 

전체 비용해서 첫 번째 금액을 할인해서 뺀다. → 카운팅은 1부터 시작

 

 

코드

const solution = (budget, product) => {
  let answer = 0;
  let n = product.length;

  product.sort((a, b) => (a[0] + a[1]) - (b[0] + b[1])); // 총합 순서대로 오름차순 정렬 먼저해주기

  // for문 돌면서 모든 상품이 한 번씩 할인받는 경우 해보기
  for (let i = 0; i < n; i++) {
    // 전체 예산 구하기
    let money = budget - (product[i][0] / 2 + product[i][1]); // 할인받은 금액을 전체 예산에서 뺀다.(산 경우로 계산)
    let count = 1; // 위의 계산으로 1개는 계산했다는 가정으로 1부터 시작함
    for (let j = 0; j < n; j++) {
      // i는 이미 할인된 상품이므로 j가 i랑 같은 경우는 안되도록 처리
      if (j !== i && (product[j][0] + product[j][1]) > money) break; // 사려고하는 것이 남은 예산보다 큰 경우 break;
      if (j !== i && (product[j][0] + product[j][1]) <= money) { // 남은예산보다 작거나 같아야한다.
        money -= (product[j][0] + product[j][1]); // 전체에서 비용 빼주기
        count++; // 상품 개수 카운팅
      }
    }
    answer = Math.max(answer, count);
  }
  return answer;
}

const budget = 28;
const product = [[6, 6], [2, 2], [4, 3], [4, 5], [10, 3]];
console.log(solution(budget, product));

주석 없는 전체코드

const solution = (budget, product) => {
  let answer = 0;
  let n = product.length;

  product.sort((a, b) => (a[0] + a[1]) - (b[0] + b[1]));

  for (let i = 0; i < n; i++) {
    let money = budget - (product[i][0] / 2 + product[i][1]); 
    let count = 1;
    for (let j = 0; j < n; j++) {
      if (j !== i && (product[j][0] + product[j][1]) > money) break;
      if (j !== i && (product[j][0] + product[j][1]) <= money) {
        money -= (product[j][0] + product[j][1]);
        count++;
      }
    }
    answer = Math.max(answer, count);
  }
  return answer;
}

const budget = 28;
const product = [[6, 6], [2, 2], [4, 3], [4, 5], [10, 3]];
console.log(solution(budget, product));

문제

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

 

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.

풀이

가로 길이 중에 가장 긴 것, 세로 길이 중에 가장 긴 것을 곱한게 최소 지갑크기가 될 수 있다.

하지만 세로길이가 가장 큰 명함이 가로로 눕혀서 수납할 수 있게된다면, 더 작은 사이즈로 만들 수 있다. 

 

각 배열에서 원소끼리 비교해서 큰 값은 0번째, 작은 값은 1번째 자리에 배치한다. 그리고 큰 값을 모아서 big이라는 배열에 push하고, 작은 값을 small이라는 배열에 push한다. 같을 경우 그대로 넣는다. 각 배열에서 Math.max() 메소드로 찾아서 곱하고 answer로 리턴한다. 

 

코드

const solution = (sizes) => {
  let answer;
  let temp;
  let small = [];
  let big = [];

  for (let i = 0; i < sizes.length; i++) {
    if (sizes[i][0] < sizes[i][1]) {
      temp = sizes[i][1];
      sizes[i][1] = sizes[i][0];
      sizes[i][0] = temp;
    }
    small.push(sizes[i][1]);
    big.push(sizes[i][0]);
  }
  
  answer = Math.max(...big) * Math.max(...small);
  return answer;
}

const sizes = [[60, 50], [30, 70], [60, 30], [80, 40]];
console.log(solution(sizes));

 

 

다른 풀이

매핑을 해서 정렬된 배열을 만들고, 바로 0번째 요소들만 매핑해서 최대값을 찾고, 1번째 요소들만 매핑해서 최대값을 찾아서 곱해서 리턴하는 방법도 있었다. 

const solution = (sizes) => {
  const newSizes = sizes.map(e => e.sort((a, b) => a - b));
  return Math.max(...newSizes.map(e => e[0])) * Math.max(...newSizes.map(e => e[1]));
}

const sizes = [[60, 50], [30, 70], [60, 30], [80, 40]];
console.log(solution(sizes));

링크드리스트 (Linked List)

이후에 다루게 되는 트리, 그래프 같은 자료 구조의 밑바탕이 되는 자료구조이다. 

배열처럼 여러 개의 값을 순서대로 지정하기 위한 자료 구조이다. 

배열에서는 데이터가 정적 공간의 메모리에 순서대로 응집되어 저장된다면, 링크드 리스트는 데이터가 메모리에 흩어져서 저장된다. 

 

선형 데이터 구조는 삽입, 삭제 연산이 엄청나게 많을 경우, 원소들의 이동작업도 그에 따라 비례한다. 이는 성능상의 문제를 일으킬 수 있기 때문에 배열이 갖고있는 메모리 사용의 비효율성 문제를 그대로 갖고있다. 

 

특징

data의 입력과 삭제가 자유롭다. 새로운 데이터를 입력할 때, 세로운 데이터는 들어가고자하는  위치에 해당하는 앞 node의 tail에 자신의 data를 연결하고, 자신의 tail을 후위 node와 연결한다. 

 

1. 요소의 삽입 삭제시에 용이하다.

2. 특정 요소 검색에 비효율적이다.

 

그러나 Linked List에서는 중간에 삽입하더라도 바로 옆 공간에 넣을 필요가 없다. 아무데나 데이터를 넣은 다음 이전 요소가 새로운 요소를 가리키도록, 그리고 새로운 요소가 다음 요소를 가리키도록 링크의 연결만 조정해주면 쉽게 요소를 중간에 끼워넣을 수 있다. 그러므로 배열에서 요소를 중간에 삽입하는 것은 O(n)의 시간이 소요되고 Linked List에서는 O(1)이 소요된다.

 

 

링크드리스트 구현하려면?

자바스크립트에서 링크드리스트는 객체를 통해 구현할 수 있다. 

예시로 두 개의 객체를 next로 연결하여 링크드리스트의 기본 구조를 만들 수 있다. 

 

 

링크드리스트의 연산

링크드리스트에서 추가, 삭제, 삽입 연산을 하기 위해서는 탐색 과정이 필요하다. 출발 지점과 끝 지점이 있어야하고, head 변수를 만들고 이 변수를 기준으로 탐색을 한다. 

const linkedList =  () => {
	this.head = null; // 처음에 노드가 없으므로 null로 초기화
    this.length = 0; // 링크드리스트의 길이
}

링크드리스트에서 데이터의 삽입, 삭제는 2가지의 과정을 거친다.

 

1. 탐색

2. 연산

 

링크드리스트에 A → B → C 순서대로 데이터가 들어있고, 우리는 D라는 데이터를 B와 C 사이에 삽입하려고 한다. 

B와 D 가 필요하다. B를 가리키는 참조값(link)가 D가 들어있는 새 노드를 가리켜야하기 때문이다. 동시에 D가 다음으로 가리키는 참조값(link)를 C를 가리키게끔 해야한다. 

 

1) 데이터 추가

'head가 null 일 때 / head가 null이 아닐 때'를 나누어서 넣어준다. 

linkedList.add = (data) => {
	const newNode = new Node(data);
    if(this.head === null) {
    	this.head = newNode;
        this.length++;
    } else {
    	let currentNode = this.head;
        while(currentNode.link !== null) {
        	currentNode = currentNode.link;
        }
        currentNode.link = newNode;
        this.length++;
    }
}

1-1) 데이터 추가 (중간노드자리)

LinkedList.prototype.insertMiddleNode = (pre, data) => {
	const newNode = new Node(data);
    let currentNode = this.head;
    
    while(currentNode.data !== pre){
    	currentNode = currentNode.link;
    }
    
    let temp = currentNode.link;
    newNode.link = temp;
    currentNode.link = newNode;
    this.length++;
}

2-1) 데이터 제거 (마지막 노드)

LinkedList.prototype.remove = () => {
	let currentNode = this.head;
    while(currentNode.link.link !== null) {
    	currentNode = currentNode.link;
    }
    currentNode.link = null;
    thiks.length--;
}

2-2) 데이터 제거 (중간 노드)

if(currentNode.data !== data) {
	while(currentNode.link.data !== data) {
    	currentNode =  currentNode.link;
    }
    temp = currentNode.link.link;
    currentNode.link.link = null;
    currentNode.link = temp;
} else {
	temp = currentNode.link;
    currentNode.link = null;
    this.head = temp;
}
this.length--;

 

 

 

 

참고

1. https://velog.io/@raram2/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%ED%86%B5%ED%95%9C-%EA%B5%AC%ED%98%84-Linked-List

2. https://taesung1993.tistory.com/10

 

'💡Algorithm' 카테고리의 다른 글

자료구조 | 자바스크립트 스택(stack)  (0) 2021.09.26
자료구조 | 해시(Hash) with JS  (0) 2021.09.24

스택 (stack)

Last In Firtst Out 먼저 들어간 자료가 나중에 나오는 후입선출 자료구조

자바스크립트에서 stack은 배열에서 데이터를 입력하는 push() 와 데이터를 제거하는 pop() 의 사용으로 구현할 수 있다. 

 

스택의 시간복잡도

삽입  O(1)
삭제 O(1)
탐색 O(n)

탐색을 할 때 n개 요소를 돌아야하기 때문에 시간복잡도는 O(n)이다. n번째 요소에 접근할 때 pop()을 n번 수행하면 n번째 요소에 접근할 수 있다. pop()의 시간복잡도는 O(1) 이므로 n번째 요소에 접근하는 시간복잡도는 O(n)이다.

 

 


예시 문제 1

괄호의 짝이 맞으면 "YES"를 리턴하고, 맞지 않으면 "NO"를 리턴해라

 

const solution = (str) => {
  // 배열을 for문으로 돌면서 (이면 push )이면 pop해서 하나도 안남아있으면 yes 리턴, 하나라도 남아있으면 no 리턴
  let answer = "YES";
  let newArr = [];

  for (let x of str) {
    if (x === '(') newArr.push(x);
    else {
      if (newArr.length === 0) {
        return "No";
      }
      newArr.pop(x);
    }
  }

  if (newArr.length === 0) {
    return answer;
  } else {
    return "NO";
  }
}

const str = "()()"
console.log(solution(str));

 

예시 문제 2

쇠막대기는 자신보다 긴 쇠막대기 위에만 놓일 수 있다. 쇠막대기를 다른 쇠막대기 위헤 놓는 경우 완전히 포함되도록 놓고, 끝점은 겹치지 않는다.

 

레이저는 괄호의 쌍으로 표현된다 =>  '( )'

쇠막대기의 왼쪽 끝은 여는 괄호 '( ' 이고 오른쪽 끝은 닫힌 괄호 ')'로 표현된다.

 

쇠막대기는 레이저에 의해 몇 개의 조각으로 잘려진다. 

쇠막대기와 레이저의 배치를 나타내는 괄호 표현이 주어졌을 때, 잘려진 쇠막대기 조각의 총 개수를 구해라. 

 

입력예제

()(((()())(())()))(())

출력예제

17

 

const solution = (str) => {
  let answer = 0;
  let stack = [];

  // 여는 괄호를 만나면 막대기의 시작점 또는 레이저의 시작점이다. -> stack에 푸시
  // 닫는 괄호를 만나면 막대기의 닫는점인지 레이저의 닫는점인지 확인해야한다 -> 바로 앞을 살펴본다

  for (let i = 0; i < str.length; i++) {
    if (str[i] === '(') { // str[i]가 여는 괄호일 경우
      stack.push(str[i]);
    } else { // str[i]가 닫는 괄호일 경우
      stack.pop();
      if (str[i - 1] === '(') { // 레이저인 경우
        // 막대기가 몇 개가 잘리는 지 answer에 카운팅 -> stack의 length 누적
        answer += stack.length;
      } else { // 막대기의 끝인 경우
        // 닫는 괄호를 만나면 무조건 하나는 pop해야한다.
        answer++;
      }
    }
  }
  return answer;
}

const str = "()(((()())(())()))(())";
console.log(solution(str));

 

 

 

'💡Algorithm' 카테고리의 다른 글

자료구조 | 링크드리스트(Linked List) with JS  (0) 2021.09.28
자료구조 | 해시(Hash) with JS  (0) 2021.09.24

1. 문제

단 한명의 선수를 제외하고는 모든 선수가 마라톤을 완주했다. 

참여 선수의 이름이 담긴 배열 participant

완주한 선수의 이름이 담긴 배열 completion

참가자 중에는 동명이인이 있을 수도 있다. 

 

입력예제 | participant ["leo", "kiki", "eden"], completion ["eden", "kiki"]

출력예제 | "leo"

 

 

2. 해시 알고리즘 생각하지 않고 풀었던 코드

const solution = (participant, completion) => {
    participant.sort();
    completion.sort();
    for (let i = 0; i < participant.length; i++) {
        if (participant[i] !== completion[i])
            return participant[i];
    }
}

const participant = ["mislav", "stanko", "mislav", "ana"];
const completion = ["stanko", "ana", "mislav"];
console.log(solution(participant, completion));

 

3. 해시 알고리즘으로 푼 코드

const solution = (participant, completion) => {
    let answer = '';

    let map1 = new Map();
    let map2 = new Map();

    for (let x of participant) {
        if (map1.has(x)) { map1.set(x, map1.get(x) + 1) }
        else { map1.set(x, 1) }
    };

    for (let x of completion) {
        if (map2.has(x)) { map2.set(x, map2.get(x) + 1) }
        else { map2.set(x, 1) }
    };

    for (let [key, value] of map1) {
        if (!map2.has(key) || map2.get(key) !== value) {
            answer = key;
        }
    }
    return answer;
}

const participant = ["mislav", "stanko", "mislav", "ana"];
const completion = ["stanko", "ana", "mislav"];
console.log(solution(participant, completion));

해시 알고리즘으로 푸는 게 효율성이 더 좋다. 

해시, 투포인터, 슬라이딩 윈도우 알고리즘을 이용해서 풀어보는 문제

 

1. 문제

주어지는 문자열에서 아나그램이 되는 부분 문자열의 개수 구하기.

부분문자열은 연속된 문자열이다. 

입력예제 | 문자열 bacaAacba, 부분 문자열 abc

출력예제 | 3 

 

 

2. 접근방법

주어진 부분문자열과 문자열의 일부(부분문자열의 length-1)를 객체로 바꾼다.

포인터 지점을 찍어놓고 for문으로 한칸씩 이동하면서 부분문자열의 length만큼의 자리를 비교한다.

아나그램이 아닐 경우 포인터를 다음 인덱스로 이동시키고,

아나그램일 경우 answer를 카운팅하고 다음인덱스로 이동시킨다. 

 

 

3. 풀이방법

1) answer에 개수를 카운팅 해주기위해서 0으로 세팅한다. 

let answer = 0;

 

2) 찾아야하는 아나그램이 되는 연속된 부분문자열이기 때문에 주어진 부분문자열의 알파벳과 값을 객체형태로 가지고 있어야한다. map 객체를 사용한다. 

let map1 = new Map();
let map2 = new Map();

for (let x of t) {
	if (map2.has(x)) {
    	map2.set(x, map2.get(x) + 1);
    } else {
    	map2.set(x, 1);
    }
}

 

3) 부분문자열의 크기만큼 슬라이딩 윈도우 기본 세팅을 해준다. 

let len = t.length -1;

 

주어진 문자열 중에서 가장 앞에 2개의 문자만 객체로 세팅한다.

length를 부문분자열의 크기만큼 하지않고 -1을 해주는 이유는

부분문자열의 크기가 3일때, 2는 고정으로 계속 두고 한자리만 바꾸면서 비교를 진행해야하기 때문이다. 

for (let i = 0; i < len; i++) {
	if (map1.has(s[i])) {
    	map1.set(s[i], map1.get(s[i] + 1);
    } else {
    	map1.set(s[i], 1);
    }
}

 

4) for문으로 슬라이딩 윈도우로 한칸씩 이동하면서

1개 추가해서 3개씩 비교하고(부분문자열의 length) 아나그램이 아닐 경우 빼주고 반복한다. 

비교 이후에 빼주는 작업을 하기위해서 투 포인터 변수 작성해준다.

let lt = 0;
rt는 for 반복문 안에 선언
for (let rt = len; rt < s.length; rt++) {
    if (map1.has(s[rt])) {
      map1.set(s[rt], map1.get(s[rt]) + 1);
    } else {
      map1.set(s[rt], 1);
    }

	// 비교
    if (compareMaps(map1, map2)) {
      answer++;
    }
    
    // 비교 이후에 왼쪽 포인터 빼주는 작업 
    map1.set(s[lt], map1.get(s[lt]) - 1); // 기존 값보다 1작게 가져와서 다시 세팅
    if (map1.get(s[lt]) === 0) {
      map1.delete(s[lt]);
    }
    lt++;
}

 

5) 비교하는 함수 작성

부분문자열의 개수가 같은지 확인

key값이 같은지, value 값이 같은지 확인

const compareMaps = (map1, map2) => {
	if (map1.size !== map2.size) {
    	return false;
    }
    for (let [key, value] of map1) {
    	if (!map2.has(key) || map2.get(key) !== value) {
        	return false;
        }
    }
    retrun true; // 모두 아니면 같은 것으므로 true 반환
}

 

 

4. 코드

const solution = (s, t) => {
  // 아나그램이 되는 연속된 부분문자열 개수
  let answer = 0;

  let map1 = new Map(); // 문자열을 map 객체로
  let map2 = new Map(); // 부분문자열을 map 객체로

  // 찾아야하는 연속된 부분문자열을 map객체로 만듬
  for (let x of t) {
    if (map2.has(x)) {
      map2.set(x, map2.get(x) + 1);
    } else {
      map2.set(x, 1);
    }
  }

  // 슬라이딩 윈도우 할 때 길이가 3이면 2개를 먼저 세팅해 놓는 것
  let len = t.length - 1;

  for (let i = 0; i < len; i++) {
    if (map1.has(s[i])) {
      map1.set(s[i], map1.get(s[i]) + 1);
    } else {
      map1.set(s[i], 1);
    }
  }

  let lt = 0;

  // 슬라이딩 윈도우이기 때문에 rt는 len길이를 시작점으로 세팅
  // 추가해서 3개로 맞춰주고
  for (let rt = len; rt < s.length; rt++) {
    if (map1.has(s[rt])) {
      map1.set(s[rt], map1.get(s[rt]) + 1);
    } else {
      map1.set(s[rt], 1);
    }

    // 3개이니까 이제 비교가능
    if (compareMaps(map1, map2)) {
      answer++;
    }

    // 비교 이후에 왼쪽 포인터 빼주는 작업 
    map1.set(s[lt], map1.get(s[lt]) - 1); // 기존 값보다 1작게 가져와서 다시 세팅
    if (map1.get(s[lt]) === 0) {
      map1.delete(s[lt]);
    }
    lt++;
  }
  return answer;
}

const compareMaps = (map1, map2) => {
  if (map1.size !== map2.size) { // 1. 개수가 같은지 확인
    return false;
  }
  for (let [key, value] of map1) { // 2. key 값이 같은지 확인
    if (!map2.has(key) || map2.get(key) !== value) { // 3. value 값이 같은지 확인
      return false;
    }
  }
  return true;
}

const str1 = "bacaAacba";
const str2 = "abc";
console.log(solution(str1, str2));

 

+ Recent posts