Algorithm - Programmers Problems (week 8)

이소라·2023년 3월 29일
0

Algorithm

목록 보기
42/77

Problem (lev. 1) : 둘만의 암호

  • 두 문자열 sskip, 그리고 자연수 index가 주어질 때, 다음 규칙에 따라 문자열을 만들려 합니다. 암호의 규칙은 다음과 같습니다.

    • 문자열 s의 각 알파벳을 index만큼 뒤의 알파벳으로 바꿔줍니다.
    • index만큼의 뒤의 알파벳이 z를 넘어갈 경우 다시 a로 돌아갑니다.
    • skip에 있는 알파벳은 제외하고 건너뜁니다.
  • 예를 들어 s = "aukks", skip = "wbqd", index = 5일 때, a에서 5만큼 뒤에 있는 알파벳은 f지만 [b, c, d, e, f]에서 'b'와 'd'는 skip에 포함되므로 세지 않습니다. 따라서 'b', 'd'를 제외하고 'a'에서 5만큼 뒤에 있는 알파벳은 [c, e, f, g, h] 순서에 의해 'h'가 됩니다. 나머지 "ukks" 또한 위 규칙대로 바꾸면 "appy"가 되며 결과는 "happy"가 됩니다.

  • 두 문자열 sskip, 그리고 자연수 index가 매개변수로 주어질 때 위 규칙대로 s를 변환한 결과를 return하도록 solution 함수를 완성해주세요.

제한사항

  • 5 ≤ s의 길이 ≤ 50
  • 1 ≤ skip의 길이 ≤ 10
  • sskip은 알파벳 소문자로만 이루어져 있습니다.
    • skip에 포함되는 알파벳은 s에 포함되지 않습니다.
  • 1 ≤ index ≤ 20

Solution

function solution(s, skip, index) {
    let answer = '';
    const regex = new RegExp(`[^${skip}]`, "g");
    const alphabet = "abcdefghijklmnopqrstuvwxyz".match(regex);
    
    for (const str of s) {
        const strIndex = (alphabet.indexOf(str) + index) % alphabet.length;
        answer += alphabet[strIndex];
    }
    return answer;
}



Problem (lev. 2) : 무인도 여행

  • 메리는 여름을 맞아 무인도로 여행을 가기 위해 지도를 보고 있습니다. 지도에는 바다와 무인도들에 대한 정보가 표시돼 있습니다. 지도는 1 x 1크기의 사각형들로 이루어진 직사각형 격자 형태이며, 격자의 각 칸에는 'X' 또는 1에서 9 사이의 자연수가 적혀있습니다. 지도의 'X'는 바다를 나타내며, 숫자는 무인도를 나타냅니다. 이때, 상, 하, 좌, 우로 연결되는 땅들은 하나의 무인도를 이룹니다. 지도의 각 칸에 적힌 숫자는 식량을 나타내는데, 상, 하, 좌, 우로 연결되는 칸에 적힌 숫자를 모두 합한 값은 해당 무인도에서 최대 며칠동안 머물 수 있는지를 나타냅니다. 어떤 섬으로 놀러 갈지 못 정한 메리는 우선 각 섬에서 최대 며칠씩 머물 수 있는지 알아본 후 놀러갈 섬을 결정하려 합니다.

  • 지도를 나타내는 문자열 배열 maps가 매개변수로 주어질 때, 각 섬에서 최대 며칠씩 머무를 수 있는지 배열에 오름차순으로 담아 return 하는 solution 함수를 완성해주세요. 만약 지낼 수 있는 무인도가 없다면 -1을 배열에 담아 return 해주세요.

제한사항

  • 3 ≤ maps의 길이 ≤ 100
  • 3 ≤ maps[i]의 길이 ≤ 100
  • maps[i]는 'X' 또는 1 과 9 사이의 자연수로 이루어진 문자열입니다.
  • 지도는 직사각형 형태입니다.

Solution

function solution(maps) {
    const answer = [];
    const n = maps.length;
    const m = maps[0].length;
    const visited = Array.from({length: n}, () => new Array(m).fill(false));
    const mapArr = maps.map((str) => str.split(''));
    
    const dx = [1, 0, -1, 0];
    const dy = [0, 1, 0, -1];
    
    const dfs = (x, y, totalFood) => {
        for (let i = 0; i < dx.length; i++) {
            const nx = x + dx[i];
            const ny = y + dy[i];
            
            if (nx >= 0 && ny >= 0 && nx < n && ny < m) {
                if (mapArr[nx][ny] !== 'X') {
                    const food = Number(mapArr[nx][ny]);
                    mapArr[nx][ny] = 'X';
                    totalFood += dfs(nx, ny, food);
                }
            }
        }
        
        
        return totalFood;
    };
    
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (mapArr[i][j] !== 'X') {
                const food = Number(mapArr[i][j]);
                mapArr[i][j] = 'X';
                const totalFood = dfs(i, j, food);
                answer.push(totalFood);
            }
        }
    }

    
    return answer.length ? answer.sort((a, b) => a - b) : [-1];
}



Problem (lev.1) : 추억 점수

  • 사진들을 보며 추억에 젖어 있던 루는 사진별로 추억 점수를 매길려고 합니다. 사진 속에 나오는 인물의 그리움 점수를 모두 합산한 값이 해당 사진의 추억 점수가 됩니다. 예를 들어 사진 속 인물의 이름이 ["may", "kein", "kain"]이고 각 인물의 그리움 점수가 [5점, 10점, 1점]일 때 해당 사진의 추억 점수는 16(5 + 10 + 1)점이 됩니다. 다른 사진 속 인물의 이름이 ["kali", "mari", "don", "tony"]이고 ["kali", "mari", "don"]의 그리움 점수가 각각 [11점, 1점, 55점]]이고, "tony"는 그리움 점수가 없을 때, 이 사진의 추억 점수는 3명의 그리움 점수를 합한 67(11 + 1 + 55)점입니다.

  • 그리워하는 사람의 이름을 담은 문자열 배열 name, 각 사람별 그리움 점수를 담은 정수 배열 yearning, 각 사진에 찍힌 인물의 이름을 담은 이차원 문자열 배열 photo가 매개변수로 주어질 때, 사진들의 추억 점수를 photo에 주어진 순서대로 배열에 담아 return하는 solution 함수를 완성해주세요.

제한사항

  • 3 ≤ name의 길이 = yearning의 길이≤ 100
    • 3 ≤ name의 원소의 길이 ≤ 7
    • name의 원소들은 알파벳 소문자로만 이루어져 있습니다.
    • name에는 중복된 값이 들어가지 않습니다.
    • 1 ≤ yearning[i] ≤ 100
    • yearning[i]는 i번째 사람의 그리움 점수입니다.
  • 3 ≤ photo의 길이 ≤ 100
    • 1 ≤photo[i]의 길이 ≤ 100
    • 3 ≤ photo[i]의 원소(문자열)의 길이 ≤ 7
    • photo[i]의 원소들은 알파벳 소문자로만 이루어져 있습니다.
    • photo[i]의 원소들은 중복된 값이 들어가지 않습니다.

Solution

function solution(name, yearning, photo) {
    const nameMap = new Map();
    
    name.forEach((name, index) => {
        nameMap.set(name, yearning[index]);
    });
    
    const answer = photo.map((arr) => arr.reduce((sum, name) => {
        const yearning = nameMap.get(name);
        if (yearning) {
            sum += yearning;
        }
        return sum;
    }, 0));
    
    return answer;
}



Problem (lev.2) : 과제 진행하기

  • 과제를 받은 루는 다음과 같은 순서대로 과제를 하려고 계획을 세웠습니다.
    • 과제는 시작하기로 한 시각이 되면 시작합니다.
    • 새로운 과제를 시작할 시각이 되었을 때, 기존에 진행 중이던 과제가 있다면 진행 중이던 과제를 멈추고 새로운 과제를 시작합니다.
    • 진행중이던 과제를 끝냈을 때, 잠시 멈춘 과제가 있다면, 멈춰둔 과제를 이어서 진행합니다.
      • 만약, 과제를 끝낸 시각에 새로 시작해야 되는 과제와 잠시 멈춰둔 과제가 모두 있다면, 새로 시작해야 하는 과제부터 진행합니다.
    • 멈춰둔 과제가 여러 개일 경우, 가장 최근에 멈춘 과제부터 시작합니다.
  • 과제 계획을 담은 이차원 문자열 배열 plans가 매개변수로 주어질 때, 과제를 끝낸 순서대로 이름을 배열에 담아 return 하는 solution 함수를 완성해주세요.

제한사항

  • 3 ≤ plans의 길이 ≤ 1,000
    • plans의 원소는 [name, start, playtime]의 구조로 이루어져 있습니다.
  • name : 과제의 이름을 의미합니다.
    • 2 ≤ name의 길이 ≤ 10
    • name은 알파벳 소문자로만 이루어져 있습니다.
    • name이 중복되는 원소는 없습니다.
  • start : 과제의 시작 시각을 나타냅니다.
    • "hh:mm"의 형태로 "00:00" ~ "23:59" 사이의 시간값만 들어가 있습니다.
    • 모든 과제의 시작 시각은 달라서 겹칠 일이 없습니다.
    • 과제는 "00:00" ... "23:59" 순으로 시작하면 됩니다. 즉, 시와 분의 값이 작을수록 더 빨리 시작한 과제입니다.
  • playtime : 과제를 마치는데 걸리는 시간을 의미하며, 단위는 분입니다.
    • 1 ≤ playtime ≤ 100
    • playtime은 0으로 시작하지 않습니다.
  • 배열은 시간순으로 정렬되어 있지 않을 수 있습니다.
    진행중이던 과제가 끝나는 시각과 새로운 과제를 시작해야하는 시각이 같은 경우 진행중이던 과제는 끝난 것으로 판단합니다.

Solution

  • stack과 queue를 사용하는 문제이고, 과제 시작 시간과 이전 종료 시간을 비교해야 하는 것은 알고 있었는데 과제 종료 후 이전 과제를 확인하는 로직이 잘 생각나지 않아서 다른 사람 풀이를 참고하여 풀었습니다. (참고)
function solution(plans) {
    const answer = [];
    const queue = plans.map((plan) => {
        const [name, starttime, playtime] = plan;
        const [hour, minute] = starttime.split(':').map(Number);
        const convertedTime = hour * 60 + minute;
        return [name, convertedTime, playtime];
    }).
    sort((a, b) => a[1] - b[1]);
    const firstPlan = queue.shift();
    let curTime = firstPlan[1];
    
    const stack = [firstPlan];
    
    while (queue.length) {
        const plan = queue.shift();
        const [_name, starttime, _playtime] = plan;
        let timeDiff = starttime - curTime;
        curTime = starttime;
        
        while (stack.length && timeDiff > 0) {
            const prevPlan = stack.pop();
            const [prevName, _prevStarttime, prevPlaytime] = prevPlan;
            
            if (prevPlaytime <= timeDiff) {
                answer.push(prevName);
                timeDiff -= prevPlaytime;
            } else {
                prevPlan[2] = prevPlaytime - timeDiff;
                timeDiff = 0;
                stack.push(prevPlan);
            }
        }
        
        stack.push(plan);
    }
    
    while (stack.length) {
        const [name, _starttime, _playtime] = stack.pop();
        answer.push(name);
    }
    return answer;
}

0개의 댓글