ericagong / algorithm_solving

2 stars 0 forks source link

[구현] 뉴스클러스터링 #49

Closed ericagong closed 1 year ago

ericagong commented 1 year ago

⭐ 성찰

  1. Array.prototype.reduce empty array로 호출 시 init value 없으면 런타임 에러 발생 ⭐ 따라서 반드시 init value 0으로 넣어주는 습관 들이기 ⭐
  2. 대/소문자 상호 변경 시 string.prototype.toUpperCase, string.prototype.toLowerCase 사용하기
  3. ⭐ 등장 횟수 셀 때, Array.prototype.filter를 통해 간단히 셀 수 있으면 우선 사용. (Map은 추후 사용)

❓ 문제 상황

뉴스클러스터링

👨‍💻 문제 해결: 등장 횟수 직접 카운팅

✅ 1차 풀이: Map 사용

  1. 교집합과 합집합의 개수를 구하기 위해 직접 Map을 만들어 개수 카운트
  2. 알파벳인지 확인하기 위한 별도 함수 생성 -> regex.test로 확인
    
    function is_alpha(str) {
    const regex = /[A-Z]{2}/
    return regex.test(str)
    }

function get_cnt(str) { let cnt = new Map() for(let i = 0; i < str.length - 1; i++) { let key = str.slice(i, i + 2).toUpperCase() if(!is_alpha(key)) continue cnt.set(key, cnt.get(key) + 1 || 1) } return cnt }

function solution(str1, str2) { let cnt_a = get_cnt(str1) let cnt_b = get_cnt(str2)

// 예외: A와 B 모두 공집합이면 자카드 유사도 1로 처리
if(cnt_a.size === 0 && cnt_b.size === 0) return 1 * 65536

let AandB = new Map()
let AorB = new Map()

for(let [k, v] of cnt_a.entries()) {
     if(cnt_b.has(k))  {
         AandB.set(k, Math.min(cnt_b.get(k), v))
         AorB.set(k, Math.max(cnt_b.get(k), v))
     }  
    else {
        AorB.set(k, v)
    }
}

for(let [k, v] of cnt_b.entries()) {
    if(!cnt_a.has(k)) {
        AorB.set(k, v)
    }
}

// reduce empty array로 호출 시 init value 없으면 에러 발생 
let AandB_num = Array.from(AandB.values()).reduce((acc, cur) => acc + cur, 0)
let AorB_num = Array.from(AorB.values()).reduce((acc, cur) => acc + cur, 0)

return parseInt(AandB_num / AorB_num * 65536) 

}


### ✅ 2차 풀이: filter를 통한 카운팅
1. Array.prorotype.filter를 통해 등장 횟수 세기
```javascript
function solution (str1, str2) {

  function explode(text) {
    const result = [];
    for (let i = 0; i < text.length - 1; i++) {
      const node = text.substr(i, 2);
      if (node.match(/[A-Za-z]{2}/)) {
        result.push(node.toLowerCase());
      }
    }
    return result;
  }

  const arr1 = explode(str1);
  const arr2 = explode(str2);
  const set = new Set([...arr1, ...arr2]);
  let union = 0;
  let intersection = 0;

  set.forEach(item => {
    const has1 = arr1.filter(x => x === item).length;
    const has2 = arr2.filter(x => x === item).length;
    union += Math.max(has1, has2);
    intersection += Math.min(has1, has2);
  })
  return union === 0 ? 65536 : Math.floor(intersection / union * 65536);
}

✅ 3차 풀이: set을 통한 counting 효율화

function getCount(str) {
    const count = new Map()
    for(let i = 0; i < str.length - 1; i++) {
        let word = str.slice(i, i+2)
        if(word.match(/[^a-zA-Z]/g)) continue
        word = word.toLowerCase()
        count.set(word, count.get(word) + 1 || 1)
    }
    return count
}

function solution(str1, str2) {
    const cnt1 = getCount(str1)
    const cnt2 = getCount(str2)

    const words = new Set()
    for(const k of cnt1.keys()) {
        words.add(k)
    }
    for(const k of cnt2.keys()) {
        words.add(k)
    }

    let aAndB = 0
    let aOrB = 0
    words.forEach((word) => {
        const a = cnt1.get(word) || 0
        const b = cnt2.get(word) || 0
        aAndB += Math.min(a, b)
        aOrB += Math.max(a, b)
    })

    const MUL = 65536;
    if(aAndB === 0 && aOrB === 0) return 1 * MUL
    else return Math.floor((aAndB / aOrB) * MUL)

}
ericagong commented 1 year ago

reviewed 👋