문제

  • 문제 링크
  • 문제 설명: 검색어에 가장 잘 맞는 웹페이지를 보여주기 위해 아래와 같은 규칙으로 검색어에 대한 웹페이지의 매칭점수를 계산 하는 것
    • 한 웹페이지에 대해서 기본점수, 외부 링크 수, 링크점수, 그리고 매칭점수를 구할 수 있다.
    • 한 웹페이지의 기본점수는 해당 웹페이지의 텍스트 중, 검색어가 등장하는 횟수이다. (대소문자 무시)
    • 한 웹페이지의 외부 링크 수는 해당 웹페이지에서 다른 외부 페이지로 연결된 링크의 개수이다.
    • 한 웹페이지의 링크점수는 해당 웹페이지로 링크가 걸린 다른 웹페이지의 기본점수 ÷ 외부 링크 수의 총합이다.
    • 한 웹페이지의 매칭점수는 기본점수와 링크점수의 합으로 계산한다.
  • 검색어 word와 웹페이지의 HTML 목록인 pages가 주어졌을 때, 매칭점수가 가장 높은 웹페이지의 index를 구하라. 만약 그런 웹페이지가 여러 개라면 그중 번호가 가장 작은 것을 반환하는 함수를 작성
  • 입력
    • pages는 길이는 1 이상 20 이하
    • 한 웹페이지 문자열의 길이는 1 이상 1,500 이하
    • 한 웹페이지의 url은 HTML의 <head> 태그 내에 <meta> 태그의 값으로 주어진다.
      • <meta property="og:url" content="https://careers.kakao.com/index" />
    • 한 웹페이지에서 모든 외부 링크는 <a href="https://URL"></a>
    • <a> 내에 다른 attribute가 주어지는 경우는 없으며 항상 href로 연결할 사이트의 url만 포함된다.
    • word의 길이는 1 이상 12 이하
    • 검색어를 찾을 때, 대소문자 구분은 무시하고 찾는다.
    • 검색어는 단어 단위로 비교하며, 단어와 완전히 일치하는 경우에만 기본 점수에 반영한다.
    • 단어는 알파벳을 제외한 다른 모든 문자로 구분한다.
  • 출력: 매칭 점수가 가장 높은 웹페이지의 인덱스

풀이

  • 먼저 각 웹 페이지마다 외부 링크를 찾아내서 모두 저장하는데 이 과정에서 페이지의 텍스트를 검사해서 기본 점수와 URL을 찾아낸다.
    • map<string, int> 자료형으로 URL 별 페이지 인덱스를 별도로 저장한다.
  • 페이지의 외부 링크를 돌면서 URL이 현재 pages 배열에 있는 페이지의 것이면 (기본 점수 / 외부 링크 수) 를 계산해서 외부 링크 페이지의 점수에 더해준다.
  • 점수 내림차순, 인덱스 오름차순으로 정렬 후 첫번째 원소의 인덱스를 반환
  • meta 태그로 페이지 URL을 탐색할 때 property와 같은 모든 속성에 대해서도 키워드를 주고 찾아야 함
    • <meta content="~"> 와 같은 것은 안됌
  • 검색어 탐색에서 대소문자를 무시하므로 맨 처음에 페이지의 모든 텍스트를 소문자로 변경하는 것이 이후 텍스트 처리하기 편함
    • transform(html.begin(), html.end(), ::tolower)
  • 외부 링크를 찾고나서 웹페이지 텍스트를 찾을 때는 body 안에서만 해도 유효하며, 알파벳 외에 모든 문자를 공백으로 치환하는 것이 좋음
    • replace_if(body.begin(), body.end(), 알파벳 아니면 true/맞으면 false 함수, 공백)
  • 정답 코드
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <sstream>
using namespace std;

const string urlPrefix = "https://";

int solution(string word, vector<string> pages) {
    map<string, int> m; // (url, page index)
    vector<string> links[pages.size()];
    vector<int> numWords(pages.size(), 0);
    vector<pair<double, int>> totalScore;
    // 검색 단어 소문자로 치환
    transform(word.begin(), word.end(), word.begin(), ::tolower);
    for (int i = 0; i < pages.size(); i++) {
        // 페이지 텍스트 모두 소문자로 치환
        string& html = pages[i];
        transform(html.begin(), html.end(), pages[i].begin(), ::tolower);
        // 페이지 URL 탐색
        string token = "<meta property=\"og:url\" content=\"" + urlPrefix;
        int tokenStart = html.find(token);
        int tokenEnd = html.find("\"", tokenStart + token.size());
        int urlStart = tokenStart + token.size() - urlPrefix.size();
        m[html.substr(urlStart, tokenEnd - urlStart)] = i;
        // body 텍스트 분리
        int bodyStart = html.find("<body>") + 6;
        int bodyEnd = html.find("</body>") - 1;
        string body = html.substr(bodyStart, bodyEnd - bodyStart);
        // 외부 링크 탐색
        token = "<a href=\"" + urlPrefix;
        tokenStart = body.find(token);
        while (tokenStart != string::npos) {
            tokenEnd = body.find("\"", tokenStart + token.size());
            urlStart = tokenStart + token.size() - urlPrefix.size();
            links[i].push_back(body.substr(urlStart, tokenEnd - urlStart));
            tokenStart = body.find(token, urlStart + links[i].back().size());
        }
        // 알파벳 외에 모두 공백으로 치환 후 단어 탐색
        replace_if(body.begin(), body.end(), [](auto& ch) { return !isalpha(ch); }, ' ');
        istringstream iss(body);
        while (getline(iss, token, ' '))
            if (token == word) numWords[i]++;
        totalScore.push_back({numWords[i], i});
    }
    
    // 외부 링크마다 pages에 존재하는 페이지면 링크 점수를 계산해서 더해줌
    for (int i = 0; i < pages.size(); i++) {
        int linkSize = links[i].size();
        for (string& l : links[i])
            if (m.find(l) != m.end())
                totalScore[m[l]].first += (double)numWords[i] / (double)linkSize;
    }
    
    // 매칭 점수 내림차순, 인덱스 오름차순으로 정렬
    sort(totalScore.begin(), totalScore.end(), [&](auto& a, auto& b) mutable {
        if (a.first == b.first) return a.second < b.second;
        return a.first > b.first;
    });
    return totalScore[0].second;
}

+ Recent posts