알고리즘 문제 풀기

프로그래머스: 매칭 점수 - javascript (정규식 끝끝판왕...)

Fo_rdang 2024. 7. 17. 13:44
반응형

문제 출처 

https://school.programmers.co.kr/learn/courses/30/lessons/42893

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제 풀이 힌트 

01. /[\d|\W]/

- [] : 문자 클래스를 정의 . 문자 클래스는 대괄호 안에 있는 문자들 중 하나와 일치하는지 검사

- \d : 숫자를 의미. [0-9]와 동일하게 작동, 숫자 하나와 일치. 

- | : 또는 을 의미하는 논리 연산자 인데, 문자 클래스 내에서 사용되었기 때문에 단순히 문자 '|' 자체로 간주됨. 

- \W: 비문자 (숫자, 영문자, 밑줄이 아닌 문자). [^a-zA-Z0-9]와 동일 

결론) 

숫자 또는 특수 문자 또는 '|'를 찾는다.

 

02. /<a href="https:\S*"/gi;

- <a href="https: 문자열과 정확히 일치하는 부분을 찾는다. 

- \S* : \S는 비공백 문자를 의미하고, *는 0번 이상 반복을 의미한다. 

- 플래그 부분

  - g: 전체 문자열 탐색 

  - i: 대소문자 구분 안함 

 

비공백 문자란? :\S

: 공백 문자가 아닌 모든 문자 

ex) 알파벳 문자, 숫자, 특수문자, 기타 모든 

 

공백 문자란? : \s

ex) ' ', 탭 \t, 줄바꿈 \n, 

 

03. flatMap 

let numbers = [1, 2, 3, 4];

let doubledNumbers = numbers.map(x => [x * 2]);
console.log(doubledNumbers); 
// 결과: [[2], [4], [6], [8]]

let flatMappedNumbers = numbers.flatMap(x => [x * 2]);
console.log(flatMappedNumbers); 
// 결과: [2, 4, 6, 8]

정답 풀이 코드 

function solution(word, pages) {
    let map = new Map(); 
    word = word.toLowerCase(); 
    pages.forEach((page, idx) => {
        let pageArr = page.split('\n'); 
        let urlIdx = pageArr.findIndex(p => p.match(/<meta property/))
        let url = pageArr[urlIdx].match(/https:\S*/)[0].slice(0,-3); //자기 자신 링크 
        let startBodyIdx = pageArr.findIndex(p => p.match(/<body>/))
        let endBodyIdx = pageArr.findIndex(p => p.match(/<\/body>/))
        let body = pageArr.slice(startBodyIdx+1, endBodyIdx)
        let wordCnt = body.flatMap(el => el.toLowerCase().split(/[\d|\W]/)).filter(e => e === word).length; //기본점수 : word 랑 일치하는 단어 수 
        let aLink = body.flatMap(el => el.match(/<a href="https:\S*"/gi)).filter(e => e !== null).map(el => el.slice(9,-1))//외부 링크 
        let point = wordCnt/aLink.length  //기본점수/외부 링크수  
        map.set(url, {idx, wordCnt, point, aLink, linkPoint:0, matchPoint:0})
        })
    
    for(let [key, value] of map.entries()){//각 링크 점수를 채우기 
        let cnt = 0; 
        value.aLink.forEach(link => {
         if(map.has(link)){
             map.get(link).linkPoint += map.get(key).point
         }
      })
    }

    let answer = [0,0]; 
     for(let [key, value] of map.entries()){
         value.matchPoint = value.wordCnt + value.linkPoint
         if(answer[1] < value.matchPoint){
             answer[0] = value.idx
             answer[1] = value.matchPoint
         }
     }
    return answer[0]
}
//기본점수 : 검색어 등장 회수 
//외부 링크 수 : 해당 => 외부 링크 수 
//링크점수 : 해당 웹페이지로 링크 걸린 웹페이지들 (기본점수/외부 링크 수) 총합
//매칭점수 : 기본점수 + 링크점수 
//return 매칭점수 가장 높은 웹페이지 index 구해라.

 

Only 정답 코드 

function solution(word, pages) {
    let map = new Map(); 
    word = word.toLowerCase(); 
    pages.forEach((page, idx) => {
        let pageArr = page.split('\n'); 
        let urlIdx = pageArr.findIndex(p => p.match(/<meta property/));
        let url = pageArr[urlIdx].match(/https:\S*/)[0].slice(0,-3); 
        let startBodyIdx = pageArr.findIndex(p => p.match(/<body>/));
        let endBodyIdx = pageArr.findIndex(p => p.match(/<\/body>/));
        let body = pageArr.slice(startBodyIdx + 1, endBodyIdx);
        let wordCnt = body.flatMap(el => el.toLowerCase().split(/[\d|\W]/)).filter(e => e === word).length;
        let aLink = body.flatMap(el => el.match(/<a href="https:\S*"/gi)).filter(e => e !== null).map(el => el.slice(9, -1));
        let point = wordCnt / aLink.length;
        map.set(url, { idx, wordCnt, point, aLink, linkPoint: 0, matchPoint: 0 });
    });

    for (let [key, value] of map.entries()) {
        value.aLink.forEach(link => {
            if (map.has(link)) {
                map.get(link).linkPoint += map.get(key).point;
            }
        });
    }

    let answer = [0, 0]; 
    for (let [key, value] of map.entries()) {
        value.matchPoint = value.wordCnt + value.linkPoint;
        if (answer[1] < value.matchPoint) {
            answer[0] = value.idx;
            answer[1] = value.matchPoint;
        }
    }
    return answer[0];
}

 

다른 정답 풀이 코드 02. 

function solution(word, pages) {
  // 검색할 단어를 소문자로 변환합니다.
  word = word.toLowerCase();

  // 정규 표현식 상수 정의
  const REGEX_WORD = /[\d|\W]/; // 단어를 분리하는 정규식
  const REGEX_URL = /<a href="https:\S*"/gi; // URL을 찾는 정규식
  const META_URL = 'meta property'; // 메타 태그를 찾기 위한 문자열

  // 각 페이지의 정보를 저장할 Map 객체를 생성합니다.
  const pageInfo = new Map();

  // 각 페이지를 순회하면서 필요한 정보를 추출합니다.
  pages.forEach((page, idx) => {
    const pageArr = page.split('\n'); // 페이지를 줄 단위로 나눕니다.
    const urlIdx = pageArr.findIndex(el => el.includes(META_URL)); // 메타 태그가 있는 줄의 인덱스를 찾습니다.
    const pageURL = pageArr[urlIdx].match(/"https:\S*"/gi)[0]; // 페이지의 URL을 추출합니다.
    const bodyStart = pageArr.findIndex(el => el.includes("<body>")); // 본문 시작 태그를 찾습니다.
    const bodyEnd = pageArr.findIndex(el => el.includes("</body>")); // 본문 종료 태그를 찾습니다.
    const body = pageArr.slice(bodyStart + 1, bodyEnd); // 본문 내용을 추출합니다.
    
    // 본문에서 검색어가 등장하는 횟수를 계산합니다.
    const point = body.flatMap(str => str.toLowerCase().split(REGEX_WORD)).filter(e => e === word).length;

    // 본문에서 외부 링크를 추출합니다.
    const outLinks = body.flatMap(str => str.match(REGEX_URL)).filter(e => e).map(e => e.substr(8, e.length));

    // 페이지 정보를 Map 객체에 저장합니다.
    pageInfo.set(pageURL, { point, outLinks, idx, matchPoint: 0 });
  });

  // 각 페이지의 링크 점수를 계산합니다.
  for (const [key, value] of pageInfo) {
    const linkPoint = value.point / value.outLinks.length; // 링크 점수를 계산합니다.

    // 각 외부 링크를 순회하며 링크 점수를 추가합니다.
    for (const link of value.outLinks) {
      if (pageInfo.has(link)) {
        const origin = pageInfo.get(link);
        const calculatedPoint = origin.matchPoint
          ? origin.matchPoint + linkPoint
          : origin.point + linkPoint;
        pageInfo.set(link, { ...origin, matchPoint: calculatedPoint });
      }
    }
  }

  // 최종 점수를 계산하여 결과를 정렬합니다.
  const answer = [];

  for (const [key, value] of pageInfo) {
    const { point, idx, matchPoint } = value;
    const finalPoint = matchPoint ? matchPoint : point;
    answer.push([idx, finalPoint]);
  }

  // 점수 기준으로 내림차순 정렬, 점수가 같으면 인덱스 기준으로 오름차순 정렬합니다.
  answer.sort((a, b) => a[1] === b[1] ? a[0] - b[0] : b[1] - a[1]);

  // 가장 높은 점수를 가진 페이지의 인덱스를 반환합니다.
  return answer[0][0];
}

Only 정답 코드 02. 

function solution (word, pages) {
  word = word.toLowerCase();
  const REGEX_WORD = /[\d|\W]/;
  const REGEX_URL = /<a href="https:\S*"/gi;
  const META_URL = 'meta property';
  const pageInfo = new Map();
  
  pages.forEach((page, idx) => {
    const pageArr = page.split('\n');
    const urlIdx = pageArr.findIndex(el => el.includes(META_URL));
    const pageURL = pageArr[urlIdx].match(/"https:\S*"/gi)[0];
    const bodyStart = pageArr.findIndex(el => el.includes("<body>"));
    const bodyEnd = pageArr.findIndex(el => el.includes("</body>"));
    const body = pageArr.slice(bodyStart+1, bodyEnd);
    const point = body.flatMap(str => str.toLowerCase().split(REGEX_WORD)).filter(e => e === word).length;
    const outLinks = body.flatMap(str => str.match(REGEX_URL)).filter(e => e).map(e => e.substr(8, e.length));
    
    pageInfo.set(pageURL, { point, outLinks, idx, matchPoint : 0 });
  });
  
  for(const [key, value] of pageInfo) {
    const linkPoint = value.point / value.outLinks.length;
    
    for(const link of value.outLinks) {
      if(pageInfo.has(link)) {
        const origin = pageInfo.get(link);
        const calculatedPoint = origin.matchPoint 
        	? origin.matchPoint + linkPoint
        	: origin.point + linkPoint;
        pageInfo.set(link, { ...origin, matchPoint: calculatedPoint });
      }
    }
  }
  
  const answer = [];
  
  for(const [key, value] of pageInfo) {
    const { point, idx, matchPoint } = value;
    const finalPoint = matchPoint ? matchPoint : point;
    answer.push([ idx, finalPoint ]);
  };
  
  answer.sort((a, b) => a[1] === b[1] ? a[0] - b[0] : b[1] - a[1]);
  
  return answer[0][0];
}

내 틀린 풀이 코드 

function solution(word, pages) {
    let map = new Map(); 
    let idxMap = new Map(); 
    
    //해당 웹 페이지 url과 idx 설정 
    //기본점수랑, 외부 링크수 정리하기 
    pages.forEach((page,idx) => {
        let url = page.match(/^  <meta property="og:url" content="https:\/\/.*/m)
        idxMap.set(url, idx); 
        let basicScore = page.match(/\bword\b/g)[0].length; 
        let alinks = page.match(/^a/g)[0]
        map.set(idx, {basicScore, links:alinks}])
    })
    
    //각 링크가 몇개 다른 웹페이지에서 언급됐는지 count++
    let linkScore = new Array(pages.length).fill(0); 
    for(let value of map.values()){
        value[1].forEach((link) => {
            let countIdx = idxMap(link)
            linkScore[countIdx]++; 
        })
    }
    //기본점수 + 링크 점수 = 매칭점수 구하기 
    let answer=[0,0]; //idx, 매칭점수  
    for(let [key, value] of map.entries()){
      let matchingScore = value[0] + linkScore[idxMap.get(key)]; 
        if(answer[1] < matchingScore) answer = [idxMap.get(key),matchingScore]; 
        else if(answer[1] === matchingScore && answer[0] > idxMap.get(key)){
           answer[0] = idxMap.get(key)
        }
    }
    return answer[0]
}
//검색 개발팀 웹 페이지의 매칭점수 
//기본점수, 외부 링크수, 링크점수, 매칭점수 
//-기본점수: 텍스트 중 검색어 등장 횟수 (대소문자 무시) 
//외부 링크수: 다른 외부 페이지로 연결된 링크 개수 
//링크점수: 해당 웹페이지가 링크가 걸림 (다른 웹페이지의 기본점수 /외부링크 수 총합)
//매칭점수:기본점수 + 링크점수 

//정규식 표현으로 얼마나 문자열을 원하는 만큼 가져오냐? 
//긴 문장을 어떻게 요약 정리하나? 

//각 모든 pages를 반복문으로 돈다. 
//idxMap 객체로 
//Map 객체로 정리한다. 
//기본점수: word가 몇개인지 센다. 
//외부 링크수: 다른 웹 페이지 링크가 몇개인지 센다. 
//Map객체를 돌면서 계산한다. 
//링크 점수: 다른 페이지에서 걸린 점수 
//매칭 점수를 구한다. 
//return 매칭 점수 가장 높은 웹페이지 index
반응형