본문 바로가기
내일배움 과제/코딩테스트

알고리즘 67번 - 둘만의 암호

by GREEN나무 2025. 1. 27.
728x90

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

JS

문제

문제 설명
두 문자열 s와 skip, 그리고 자연수 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"가 됩니다.

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

제한사항
5 ≤ s의 길이 ≤ 50
1 ≤ skip의 길이 ≤ 10
s와 skip은 알파벳 소문자로만 이루어져 있습니다.
skip에 포함되는 알파벳은 s에 포함되지 않습니다.
1 ≤ index ≤ 20
입출력 예
s skip index result
"aukks" "wbqd" 5 "happy"
입출력 예 설명
입출력 예 #1
본문 내용과 일치합니다.

 

 


계획

문자열을 배열로 만들기

제외목록의 알파벳인지 확인

문자열 s의 각 알파벳을 index만큼 뒤의 알파벳으로 바꾸기

 


참고, 풀이

const char = 'A'; // 변환할 문자
const asciiCode = char.charCodeAt(0); // 첫 번째 문자의 아스키코드 반환
console.log(asciiCode); // 출력: 65

 

const asciiCode = 65; // 변환할 아스키코드 번호
const char = String.fromCharCode(asciiCode); // 아스키코드를 문자로 변환
console.log(char); // 출력: 'A'

function solution(s, skip, index) {
    // 문자열을 배열로 만들기
    let sArr = [...s];
    // 제외목록을 아스키코드 배열로 변환하고 정렬하기
    const skipArr = [...skip].map((char) => char.charCodeAt(0)).sort((a, b) => a - b);

    // 각 문자를 변환
    let result = sArr.map((char) => {
        let charCode = char.charCodeAt(0); // 변환할 문자의 아스키코드
        let steps = 0; // 실제 이동한 거리

        // 제외 목록을 고려하여 실제 이동할 거리 계산
        for (let i = 0; steps < index; i++) {
            charCode++; // 알파벳 이동
            if (charCode > 122) charCode = 97; // 'z' 다음에는 'a'로 순환

            // 제외 목록에 없는 경우에만 steps 증가
            if (!skipArr.includes(charCode)) {
                steps++;
            }
        }

        // 변환된 문자 반환
        return String.fromCharCode(charCode);
    });

    // 배열을 문자열로 변환하여 반환
    return result.join("");
}

코드 간략화하기

function solution(s, skip, index) {
    const skipSet = new Set([...skip].map((c) => c.charCodeAt(0))); // 제외 목록을 Set으로 저장
    return [...s].map((c) => {
        let charCode = c.charCodeAt(0);
        for (let steps = 0; steps < index; charCode++) {
            if (charCode > 122) charCode = 97; // 'z' 다음에는 'a'로 순환
            if (!skipSet.has(charCode)) steps++;
        }
        return String.fromCharCode(charCode);
    }).join('');
}

다른사람 답

function solution(s, skip, index) {
    const alphabet = [...'abcdefghijklmnopqrstuvwxyz'].filter(c => !skip.includes(c));
    return s.split("").map(c => alphabet[(alphabet.indexOf(c) + index) % alphabet.length]).join("");
}
function solution(s, skip, index) {
    const skipSet = new Set(skip); // skip 문자열의 문자를 Set으로 변환하여 검색 효율화
    const alphabet = 'abcdefghijklmnopqrstuvwxyz'; // 알파벳 전체 문자열
    const filtered = [...alphabet].filter(ch => !skipSet.has(ch)); // skip 문자를 제외한 유효 알파벳 배열

    return [...s].map(char => {
        const currentIndex = filtered.indexOf(char); // 현재 문자의 위치
        const newIndex = (currentIndex + index) % filtered.length; // 이동 후의 위치
        return filtered[newIndex]; // 변환된 문자 반환
    }).join(''); // 최종 문자열 반환
}
function solution(s, skip, index) {
    const able = 'abcdefghijklmnopqrstuvwxyz'
    .replace(new RegExp(skip.split('').join('|'), 'g'), '');
    return s
        .split('')
        .map(e =>  able[(able.indexOf(e) + index) % able.length])
        .join('');
}

주어진 문자 집합(able)에서 특정 문자를 건너뛰고(skip), 주어진 index만큼 이동한 결과를 반환하는 방식

1. able 변수 생성

const able = 'abcdefghijklmnopqrstuvwxyz'
  .replace(new RegExp(skip.split('').join('|'), 'g'), '');
  • 'abcdefghijklmnopqrstuvwxyz': 알파벳 소문자들을 문자열로 나타냄.
  • skip.split('').join('|'): skip 문자열을 문자 하나씩 나눈 뒤, 이를 정규식으로 사용할 수 있도록 '|'(OR 연산자)로 연결.
    • 예: skip이 "aeiou"라면 결과는 "a|e|i|o|u".
  • new RegExp(..., 'g'): 정규식을 생성하고, 문자열 전체에서(g: global) 일치하는 부분을 찾아 제거.
  • replace: skip에 포함된 문자를 able 문자열에서 제거.
    • 예: skip = "aeiou"라면 able은 "bcdfghjklmnpqrstvwxyz"가 됨.

2. s 문자열 변환

return s
  .split('')
  .map(e => able[(able.indexOf(e) + index) % able.length])
  .join('');
  • s.split(''): 문자열 s를 각 문자로 나눠 배열로 만듦.
    • 예: s = "abc"라면 결과는 ['a', 'b', 'c'].
  • .map(...): 배열의 각 요소(e)를 변환.
    • able.indexOf(e): e 문자가 able에서 몇 번째 위치인지 찾음.
      • 예: able = "bcdfghjklmnpqrstvwxyz", e = 'b'라면 able.indexOf('b')는 0.
    • (able.indexOf(e) + index) % able.length: index만큼 이동한 새로운 위치를 계산. % able.length는 배열의 길이를 넘어가는 경우 순환하도록 함.
      • 예: able.length = 21이고, index = 2라면, 이동 후 위치는 (현재 위치 + 2) % 21.
    • able[...new index...]: 새로운 위치에 있는 문자를 가져옴.
  • .join(''): 변환된 문자 배열을 다시 문자열로 합침.

참고

https://hianna.tistory.com/409