[프로그래머스 lev2/JS] 뉴스 클러스팅

woolee의 기록보관소·2022년 11월 1일
0

알고리즘 문제풀이

목록 보기
31/178

문제 출처

프로그래머스 lev2 - 뉴스 클러스팅

나의 풀이

1차 시도

배열 내 중복 문자열에 대한 처리가 안 된다.

function solution(str1, str2) {
  let regExp = /[^a-zA-Z]/g;
  // str1=str1.replace(regExp, '');
  // str2=str2.replace(regExp, '');
  let s1 = [];
  let s2 = [];

  for (let i=2; i<=str1.length; i++) {
    if (!str1.slice(i-2,i).match(regExp)) {
      s1.push(str1.slice(i-2,i).toUpperCase());
    }
  }

  for (let i=2; i<=str2.length; i++) {
    if (!str2.slice(i-2,i).match(regExp)) {
      s2.push(str2.slice(i-2,i).toUpperCase());
    }
  }

  console.log(s1)
  console.log(s2)


  let is = s1.filter(x => s2.includes(x)).length;
  let exSum = s1.filter(x => !s2.includes(x)).concat(s2.filter(x => !s1.includes(x))).length;

  console.log(is)
  console.log(exSum)

  if (is==0 && exSum==0) {
    return 65536;
  }
  else return parseInt((is / (is+exSum))*65536); 
}

// console.log(solution("handshake", "shake hands"));
// console.log(solution("FRANCE", "french"));
// console.log(solution("E=M*C^2", "e=m*c^2"));
console.log(solution("aa1+aa2", "AAAA12"));

2차 시도

61.5 / 100

set으로 유일한 값을 저장하려 했는데 new Set이 안 먹히길래 for문을 돌렸다.

function solution(str1, str2) {
  let regExp = /[^a-zA-Z]/g;
  let s1 = [];
  let s2 = [];

  for (let i=2; i<=str1.length; i++) {
    if (!str1.slice(i-2,i).match(regExp)) {
      s1.push(str1.slice(i-2,i).toUpperCase());
    }
  }

  for (let i=2; i<=str2.length; i++) {
    if (!str2.slice(i-2,i).match(regExp)) {
      s2.push(str2.slice(i-2,i).toUpperCase());
    }
  }

  console.log(s1)
  console.log(s2)

  let set = [...s1];
  for (let i=0; i<s2.length; i++) {
    if (!set.includes(s2[i])) {
      set.push(s2[i]);
    }
  }

  // const set = new Set([...s1], [...s2]);
  console.log(set)

  let union = 0;
  let intersection = 0;

  set.forEach(v => {
    const tmp1 = s1.filter(e => e === v).length;
    const tmp2 = s2.filter(e => e === v).length;

    union += Math.max(tmp1, tmp2);
    intersection += Math.min(tmp1, tmp2);
  })

  return union === 0 ? 65536 : Math.floor(intersection/union*65536);
}

// console.log(solution("handshake", "shake hands"));
console.log(solution("FRANCE", "french"));
// console.log(solution("E=M*C^2", "e=m*c^2"));
// console.log(solution("aa1+aa2", "AAAA12"));

3차 시도 (통과코드)

2차 시도 풀이에, 변수 set을 배열이 아닌 new Set으로 다시 한 번 감싸줬다.
set = new Set(set); 만 추가했더니 통과 되었다.

1. 문자열을 제외한 나머지를 의미하는 정규 표현식을 만들어 준 뒤, 해당 정규표현식에 해당하는 애들을 제외한 나머지(즉, 문자열로만 구성된 애들)를 s1, s2에 각각 push 해준다.

2. s1, s2 각각에서 값들을 1개만 갖는 애들을 만들기 위해 new Set을 사용한다. 근데 나는 중간에 `const set = new Set([...s1], [...s2])`가 안 먹혀서 for문으로 순회한 뒤 new Set()으로 한번 더 묶어주었다.


3. 유일한 값을 갖는 set을 순회하면서 각각의 s1, s2 배열에서 각 set 요소들과 일치하는 애들의 개수롤 tmp1, tmp2 변수에 담고 최대값은 union(합집합)에 담고, 최소값은 intersection(교집합)에 담는다.


** set은 중복이 없는 요소들로 구성되어 있고, index 개념이 없다. 반면 배열은 중복이 있을 수 있고, index 개념이 존재한다.
function solution(str1, str2) {
  // 1. 
  let regExp = /[^a-zA-Z]/g;
  let s1 = [];
  let s2 = [];

  for (let i=2; i<=str1.length; i++) {
    if (!str1.slice(i-2,i).match(regExp)) {
      s1.push(str1.slice(i-2,i).toUpperCase());
    }
  }

  for (let i=2; i<=str2.length; i++) {
    if (!str2.slice(i-2,i).match(regExp)) {
      s2.push(str2.slice(i-2,i).toUpperCase());
    }
  }

  console.log(s1)
  console.log(s2)

  // 2.
  let set = [...s1];
  for (let i=0; i<s2.length; i++) {
    if (!set.includes(s2[i])) {
      set.push(s2[i]);
    }
  }

  set = new Set(set);

  // const set = new Set([...s1], [...s2]);
  console.log(set)

  // 3. 
  let union = 0;
  let intersection = 0;

  set.forEach(v => {
    const tmp1 = s1.filter(e => e === v).length;
    const tmp2 = s2.filter(e => e === v).length;

    union += Math.max(tmp1, tmp2);
    intersection += Math.min(tmp1, tmp2);
  })

  return union === 0 ? 65536 : Math.floor(intersection/union*65536);
}

// console.log(solution("handshake", "shake hands"));
console.log(solution("FRANCE", "french"));
// console.log(solution("E=M*C^2", "e=m*c^2"));
// console.log(solution("aa1+aa2", "AAAA12"));

교집합, 합집합에 대해 처음엔 아래처럼 filter, includes를 활용해 구하려고 했으나 실패했다. 합집합 = (교집합+배타적논리합)

filter와 includes를 통해 구하면, 중복에 요소에 대한 합집합을 구하기가 어렵다. 예를 들어, 위 tc처럼 [aa, aa]와 [aa, aa, aa] 간 합집합은 [aa, aa, aa]가 나와야 하지만, 아래처럼 filter과 includes를 조합하면 []가 나온다. 왜냐하면 각 배열에 대해 무작위로 비교하기 때문이다.

차집합 :

let difference = arr1.filter(x => !arr2.includes(x));

교집합 :

let difference = arr1.filter(x => arr2.includes(x));

배타적 논리합 :

let difference = arr1
                 .filter(x => !arr2.includes(x))
                 .concat(arr2.filter(x => !arr1.includes(x)));

다른 풀이

닉네임 '패터쓴'님의 풀이

알파벳 판별에 있어 대소 비교를 통해서도 판별할 수 있다.

function solution(str1, str2) {
  // 물밑작업하자
  const a = prep(str1), b = prep(str2);

  // 교, 합집합을 찾아보자
  const int = {}, uni = {};

  // a집합을 먼저 둘러보자
  for (const p in a){
      if(b[p] === undefined) {
          // b 집합에는 없다! => 합집합에만 넣자
          uni[p] = a[p];
      } else {
          // 양쪽에 다있다! => 양쪽에 다넣자
          // 교집합의 경우 더 작은 수를, 합집합의 경우 더 큰 수를 넣자.
          int[p] = Math.min(a[p], b[p]);
          uni[p] = Math.max(a[p], b[p]);
      }
  }
  // 이 단계에서 교집합은 완료 되었다

  // b 집합에만 있는 녀석들을 합집합에 추가해주자
  for (const p in b){
      if(uni[p] === undefined) {
          uni[p] = b[p];
      }
  }

  // 갯수를 셔보자
  let iC = 0, uC = 0;

  for (const p in int) iC += int[p];
  for (const p in uni) uC += uni[p];

  // 합집합이가 공집합이면 65536을, 아니면 시키는대로 해서
  // 돌려주자 (~~ 는 양수에 적용 가능한 플로어링 테크닉임)

  return uC ? ~~(65536*iC/uC) : 65536;
}

function prep(str){
  // 대소문자 동일 취급
  str = str.toLowerCase();

  // {요소: 횟수}로 매핑해보자
  const ans = {};
  for (let i = 0; i < str.length-1; i++){
      // 두 캐릭터 중에 하나라도 알파벳 아니면 무시하자
      if (str[i] < 'a' || 'z' < str[i]) continue;
      if (str[i+1] < 'a' || 'z' < str[i+1]) continue;

      // 이 요소를 key로, 이 요소의 등장횟수를 value로 기록하자
      const el = str[i]+str[i+1];
      ans[el] = (ans[el]||0) + 1;
  }

  // 돌려주자
  return ans;
}

// console.log(solution("handshake", "shake hands"));
console.log(solution("FRANCE", "french"));
// console.log(solution("E=M*C^2", "e=m*c^2"));
// console.log(solution("aa1+aa2", "AAAA12"));
profile
https://medium.com/@wooleejaan

0개의 댓글