개발조각

[프로그래머스] 시저 암호 본문

알고리즘🅰/프로그래머스

[프로그래머스] 시저 암호

개발조각 2022. 2. 24. 15:22
728x90
반응형

이번 문제는 어려운 편은 아닌데 묘하게 조건이 헷갈려서 헤맸던 문제인 것 같아요.

 

해결방안 설명하기 전에 이 문제는 주의해야 될 점이 꽤 있어서 주의 사항부터 설명하겠습니다.

주의 사항

  • "z"는 1만큼 밀면 "a"가 됩니다.

제한 조건

  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다. -> 알파벳 소문자, 대문자, 공백만 비교하면 됨
  • n은 1 이상, 25이하인 자연수입니다. -> n이 25일 경우도 생각해봐야 됨

이 4가지 사항을 주의하면서 코드를 풀어야 됩니다.

입출력 예에서 웬만한 조건에 대해서는 잘 설명이 되어 있는데요.

여기서 또 생각해 봐야 되는 부분이 있습니다.

만약 s = "CD"; n=25;이면 어떻게 될까요?

답 : s="BC"

이렇게 됩니다.

 

그러므로 단순히 n만큼 밀때만 생각하는 것이 아니라

문자열s의 문자에 n만큼 밀 때 z이후일 경우에도 생각해봐야 됩니다.

이 경우도 생각하시고 풀어야 제출 후 채점하기에서 정확성 테스트에 통과하게 됩니다.


해결방안

function solution(s, n) {    
    let upper =`ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
    let lower = `abcdefghijklmnopqrstuvwxyz`;
    var answer = '';
    
    for(let i = 0; i<s.length; i++){
        let uppIdx = upper.indexOf(s[i]);
        let lowIdx = lower.indexOf(s[i]);
        
        if(s[i] === ' ')answer += ' ';
        else if(uppIdx !== -1){
            uppIdx+n > 25 ? answer += upper[uppIdx-26+n] : answer += upper[uppIdx+n];
        }else{
            lowIdx+n > 25 ? answer += lower[lowIdx-26+n] : answer += lower[lowIdx+n];
        }
    }
    return answer;
}

해결방안 순서

  1. 대문자 A~Z까지 upper에 문자열로 담기, 소문자 a~z까지 lower에 문자열로 담기
  2. for문, 조건문을 사용해서 시저 암호 작동되게 만들기

 

1단계. 대문자 A~Z까지 upper에 문자열로 담기, 소문자 a~z까지 lower에 문자열로 담기

let upper =`ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
let lower = `abcdefghijklmnopqrstuvwxyz`;
var answer = '';
  • let upper =`ABCDEFGHIJKLMNOPQRSTUVWXYZ`; : 대문자 A~Z까지 담음
  • let lower = `abcdefghijklmnopqrstuvwxyz`; : 소문자 a~z까지 담음
  • var answer = ''; s문자열에 시저 암호를 적용한 값을 담아줄 문자열 변수

먼저 대소문자를 각각 문자열로 담았습니다.

문자열 s에는 대소문자가 섞여있기 때문에 대문자, 소문자를 따로 비교하기 위해 담아주었습니다.

 

여기서 upper, lower변수를 배열이 아닌 문자열까지만 쓴 이유는

upper[3] = "D"

이렇게 문자열에서도 배열처럼 index를 적용할 수 있기 때문에 배열을 쓸 필요가 없어 문자열로 썼습니다.

 

2단계. for문, 조건문을 사용해서 시저 암호 작동되게 만들기

for(let i = 0; i<s.length; i++){
    let uppIdx = upper.indexOf(s[i]);
    let lowIdx = lower.indexOf(s[i]);

    if(s[i] === ' ')answer += ' ';
    else if(uppIdx !== -1){
        uppIdx+n > 25 ? answer += upper[uppIdx-26+n] : answer += upper[uppIdx+n];
    }else{
        lowIdx+n > 25 ? answer += lower[lowIdx-26+n] : answer += lower[lowIdx+n];
    }
}
return answer;

이 코드 부터는 시저 암호를 적용되게 만드는 코드입니다.

먼저 for문을 돌려 문자열s의 각각의 문자를 순서대로 제어할 수 있게 했습니다.

이점을 이용해서 조건문으로

  • 문자가 공백일 경우 공백으로
  • 문자가 대문자일 경우 대문자에서 n만큼 밀어주기
  • 문자가 소문자일 경우 소문자에서 n만큼 밀어주기

이런 식으로 구해주었습니다.

여기서 문자가 대문자와 소문자일 경우

s문자열의 문자에 n만큼 밀 때 z보다 클경우에 대해서도 조건문으로 제어해 주었습니다.

 

for(let i = 0; i<s.length; i++)

for문을 사용하여  문자열s의 각각의 문자를 순서대로 제어할 수 있게 했습니다.

테스트 1 (s = "AB"; n=1;)
for(let i = 0; i<s.length; i++)
-> s.length = 2
-> 0, 1 두번 반복

테스트 2 (s = "z"; n=1;)
for(let i = 0; i<s.length; i++)
-> s.length = 1
-> 0 한번 반복

테스트 3 (s = "a B z"; n=4;)
for(let i = 0; i<s.length; i++)
-> s.length = 5
-> 0, 1, 2, 3, 4 다섯번 반복

 

let uppIdx = upper.indexOf(s[i]);
let lowIdx = lower.indexOf(s[i]);

  • let uppIdx = upper.indexOf(s[i]); : 문자열s의 i번째 문자가 upper(대문자문자열)에서 몇번째 문자인지 찾기
  • let lowIdx = lower.indexOf(s[i]); : 문자열s의 i번째 문자가 lower(소문자문자열)에서 몇번째 문자인지 찾기

uppIdx, lowIdx변수를 만들어준 이유는

  1. 깔끔하게 정리하기 위해서
  2. 대문자, 소문자, 공백을 구분하기 위해
  3. 위에 두 변수를 활용해서 아래 코드에 있는 조건문에 사용하기 위해
  4. indexOf(s[i]);을 넣어주어 s문자열의 i번째 문자가 upper, lower문자열에 몇 번째 위치에 있는지 찾도록 하기 위해

이러한 이유로 uppIdx, lowIdx변수를 만들어 주었습니다.

 

indexOf메서드에 대해서는 전에 "[프로그래머스] 문자열 내 p와 y의 개수"에서도 소개했는데요.

궁금하시면 아래 링크에서 확인하면 됩니다.👇

https://development-piece.tistory.com/20

 

[프로그래머스] 문자열 내 p와 y의 개수

이번 문제는 쉬운 편에 속하는 것 같아요. 프로그래머스에서 반복문은 항상 for문만 썼는데 이번에는 while문을 써서 풀어보았고 함수로 담는게 더 깔끔한 것 같아서 함수를 만들어보았습니다. 해

development-piece.tistory.com

 

그렇지만 보기 귀찮을 것 같아서 또 설명하겠습니다.

MDN Web Docs
String.prototype.indexOf()
indexOf() 메서드는 호출한 String 객체에서 주어진 값과 일치하는 첫 번째 인덱스를 반환합니다. 일치하는 값이 없으면 -1을 반환합니다.

구문 : str.indexOf(searchValue[, fromIndex])

  • searchValue :찾으려는 문자열. 아무 값도 주어지지 않으면 문자열 "undefined"를 찾으려는 문자열로 사용합니다.
  • fromIndex Optional : 문자열에서 찾기 시작하는 위치를 나타내는 인덱스 값입니다. 어떤 정수 값이라도 가능합니다. 기본값은 0이며, 문자열 전체를 대상으로 찾게 됩니다. 만약 fromIndex 값이 음의 정수이면 전체 문자열을 찾게 됩니다. 만약 fromIndex >= str.length 이면, 검색하지 않고 바로 -1을 반환합니다. searchValue가 공백 문자열이 아니라면, str.length를 반환합니다.
// String.prototype.indexOf() 예제
const s = "ppoooy"
console.log(s.indexOf("p")); // 0
console.log(s.indexOf("o")); // 2
console.log(s.indexOf("y")); // 5
console.log(s.indexOf("p", 1)); // 1
console.log(s.indexOf("p", 2)); // -1

여기서 중요한 점은 "일치하는 값이 없으면 -1을 반환합니다."는 점입니다.

"프로그래머스 문자열 내 p와 y의 개수"를 풀 때도 이점을 활용했었는데, 이번에도 -1이라는 점을 사용할 예정입니다.

 

s[i]가 공백이면

  • uppIdx  = -1
  • lowIdx  = -1

s[i]가 대문자이면

  • uppIdx  = -1이 아닌 숫자가 나옴
  • lowIdx  = -1

s[i]가 소문자이면

  • uppIdx  = -1
  • lowIdx  = -1이 아닌 숫자가 나옴

이런 식으로 s[i]가 공백, 대문자, 소문자일때마다 -1이 다르게 나옵니다.

그리고 s[i] 문자가 upper, lower배열에 몇 번째에 위치하는지도 확인할 수 있습니다.

let uppIdx = upper.indexOf(s[i]);
let lowIdx = lower.indexOf(s[i]);

테스트 1 (s = "AB"; n=1;)
i = 0일 때 : uppIdx = 0 , lowIdx = -1 -> 대문자, s[0] === upper[0]
i = 1일 때 : uppIdx = 1, lowIdx = -1 -> 대문자, s[1] === upper[1]

테스트 2 (s = "z"; n=1;)
i = 0일 때 : uppIdx = -1, lowIdx = 25 -> 소문자, s[0] === lower[25]

테스트 3 (s = "a B z"; n=4;)
i = 0일 때 : uppIdx = -1, lowIdx = 0 -> 소문자, s[0] === lower[0]
i = 1일 때 : uppIdx = -1, lowIdx = -1 -> 공백
i = 2일 때 : uppIdx = 1, lowIdx = -1 -> 대문자, s[1] === upper[1]
i = 3일 때 : uppIdx = -1, lowIdx = -1 -> 공백
i = 4일 때 : uppIdx = -1, lowIdx = 25 -> 소문자, s[4] === lower[25]

이점을 사용해서 다음코드 조건문에 적용해보겠습니다.

 

if(s[i] === ' ')
else if(uppIdx !== -1)
else

  • if(s[i] === ' ') -> s[i] 공백일 경우
  • else if(uppIdx !== -1) -> s[i] 대문자일 경우
  • else -> s[i] 소문자일 경우

위에 설명에서 만든 uppIdx, lowIdex를 활용하여 조건문을 만들어 주었습니다.

  • 공백은 s[i] === ' '만 해줘도 s[i]가 공백인지 아닌지 구분할 수 있어서 s[i] === ' ' 썼습니다.
  • 대문자는 앞에서 uppIdx가 -1이 아니면 대문자라고 했습니다. 그래서 uppIdx === -1 썼습니다.
  • 제한사항에서 "s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다."라고 했습니다. 그래서 위에 if, else if에서 공백, 대문자에 대한 처리를 해주었기 때문에 else만 썼습니다.

 

공백일 때 : answer += ' ';

공백은 제한 조건에서 "공백은 아무리 밀어도 공백입니다."이라고 했기 때문에 answer += ' ';을 써주었습니다.

 


대문자일 때 : uppIdx+n > 25 ? answer += upper[uppIdx-26+n] : answer += upper[uppIdx+n];
소문자 일 때 : lowIdx+n > 25 ? answer += lower[lowIdx-26+n] : answer += lower[lowIdx+n];

대문자와 소문자일 때는 단순히 n만큼 밀어주면 끝나는 것이 아니라

주의사항에서 말했던 대로 "문자열s의 문자에 n만큼 밀 때 z이후일 경우"도 생각해서 조건문을 써주어야 됩니다.

 

저는 한눈에 보기 쉽게 삼항 연산자를 사용해 주었습니다.

삼항 연산자

구문 : 조건 ? true : false

  • 조건이 true이면 true를 실행
  • 조건이 false이면 false를 실행

설명할 때는 대문자 위주로만 설명하겠습니다.

(소문자는 대문자에서 변수만 바꿔주면 되기 때문에 소문자에 대한 설명은 안 하겠습니다.)

 

조건 : uppIdx+n > 25

  • uppIdx+n > 25 : 문자열s의 문자에 n만큼 밀때 Z이후일 경우
  • uppIdx+n : Z이상일 경우

그리고 uppIdx+n이 25보다 커야 Z다음에 다시 A부터 문자를 받을 수 있습니다.

  • 문자 = 'Y' -> uppIdx = 24, n=1 -> uppIdx+n =25 -> upper[25] -> z
  • 문자 = 'Z' -> uppIdx = 25, n=1 -> uppIdx+n =26 -> upper[26] -> a

 

참 : answer += upper[uppIdx-26+n]

"문자열s의 문자에 n만큼 밀 때 z이후일 경우"가 참일 경우 Z다음 다시 A부터 세어서 알파벳을 반환해 주어야 됩니다.

  • uppIdx-26+n = uppIdx-25+n -1

오른쪽에 있는 답을 합쳐서 왼쪽식으로 썼습니다.

uppIdx-25+n -1

먼저 25를 빼준이후는 uppIdx+n이 25보다 크기 때문에 다시 처음으로 돌아가 개수를 세주기 위해 25를 빼주었습니다.

그다음 n만큼 밀어줘야 되기 때문에 더해 주었고요.

마지막으로 -1을 넣어준 이유는 Z에서 A로 갈 때 1이 밀린 거라 그 1을 빼준 겁니다.

 

거짓 : answer += upper[uppIdx+n];

"문자열s의 문자에 n만큼 밀때 z이후일 경우"가 아닐 경우는 n만큼 밀어주면 되기 때문에 +n을 넣어주었습니다.

테스트 1 (s = "AB"; n=1;)
i = 0일 때
uppIdx = 0 , lowIdx = -1 -> 대문자, s[0] === upper[0]
uppIdx+n > 25 -> 0+1 >25 -> false임으로 answer += answer += upper[uppIdx+n] 실행
answer += upper[0 + 1]; -> answer += upper[1]; -> "B"

i = 1일 때
uppIdx = 1, lowIdx = -1 -> 대문자, s[1] === upper[1]
uppIdx+n > 25 -> 1+1 >25 -> false임으로 answer += upper[uppIdx+n] 실행
answer += upper[1+1]; -> answer += upper[2]; -> "C"

answer ="BC"

테스트 2 (s = "z"; n=1;)
i = 0일 때
uppIdx = -1, lowIdx = 25 -> 소문자, s[0] === lower[25]
lowIdx+n > 25 -> 25+1 >25 -> true임으로 answer += lower[lowIdx-26+n] 실행
answer += lower[25-26+1] -> answer += lower[0] -> "a"

answer ="a"

테스트 3 (s = "a B z"; n=4;)
i = 0일 때
uppIdx = -1, lowIdx = 0 -> 소문자, s[0] === lower[0]
lowIdx+n > 25 -> 0+4 >25 -> false임으로 answer += lower[lowIdx+n] 실행
answer += lower[0+4] -> answer += lower[4] -> "e"

i = 1일 때
uppIdx = -1, lowIdx = -1 -> 공백 -> answer += ' ' 실행

i = 2일 때
uppIdx = 1, lowIdx = -1 -> 대문자, s[1] === upper[1]
uppIdx+n > 25 -> 1+4 >25 -> false임으로 answer += answer += upper[uppIdx+n] 실행
answer += upper[1 + 4]; -> answer += upper[5]; -> "F"

i = 3일 때
uppIdx = -1, lowIdx = -1 -> 공백 -> answer += ' '실행

i = 4일 때
uppIdx = -1, lowIdx = 25 -> 소문자, s[4] === lower[25]
lowIdx+n > 25 -> 25+4 >25 -> true임으로 answer += lower[lowIdx-26+n] 실행
answer += lower[25-26+4] -> answer += lower[3] -> "d"

answer = "e F d"

여기까지 프로그래머스 시저 암호 해결방안에 대해 설명해보았습니다.

728x90
반응형
Comments