[프로그래머스 - Lv2] 조이스틱 (JS)

빛트·2022년 6월 14일
1

ALGORITHM-PROGRAMMERS

목록 보기
2/4
post-thumbnail

첫번째 시도 ( 실패 )

처음엔 단순하게 생각해서 왼쪽, 오른쪽으로만 나눠서 진행해 봤습니다

function solution(name) {
    // '~~~~AAA'와 같이 마지막에 'A'가 위치할 경우 쉽게 처리하기 위해 역순으로 계산합니다
    let rProcess = name.split("").reverse();
    let lProcess = [...name.split("").splice(1), name[0]];
    
    // 마지막 시행 이후에는 죄우 이동이 필요 없으므로 -1부터 시작합니다
    let rCount = rProcess.reduce(controllStick,-1);
    let lCount = lProcess.reduce(controllStick,-1);
    
    let min = Math.min(rCount, lCount);
    
    // 'AAA'와 같이 조작이 필요 없을 경우 -1이 반환되게 되므로 +1을 해줍니다
    if(min === -1){
        min += 1;
    }
    
    return min;
}

const controllStick = (prev, curr, i) => {
    if(prev === -1 && curr ==='A'){
           return prev;
    }

    let charCode = curr.charCodeAt();
    return prev + Math.min(charCode - 65,  91 - charCode) + 1;
}

생각보다는 많이 통과하네요

11, 13, 14, 18, 20, 22, 23, 24, 25, 26, 27번 테스트에서 실패했습니다

'ABAAAAAAB'와 같이 진행방향의 반대방향으로 돌아가야 하는 경우도 고려해야 합니다


두번째 시도 ( 성공 )

구상

0. 모든 문자가 A인 경우 0을 반환하고 종료합니다

1. 양방향으로 분기합니다

2. A를 만나면 분기합니다

다만 이 때 조이스틱이 움직인 횟수를 잘 생각해야 합니다

진행방향이 바뀌는 경우 3번 인덱스를 기준으로

조이스틱을 ( L ) 로만 움직여도 되는 걸 ( R L L ) 와 같이 움직여줬으므로

그만큼 count에서 다시 빼줘야 합니다

2-1. 이전 수행에서의 문자가 A인 경우 진행방향을 유지합니다

이전 수행에서의 원본 문자가 A인 경우 진행방향을 유지합니다

A들 틈에서 방황하다가는 무한루프에 빠져벌일것입니다

3. 전부 A가 되면 조이스틱을 움직인 횟수를 반환합니다

코드

function solution(name) {
  let arr = name.split("");

  if (!hasNextStep(arr)) {
    return 0;
  }

  let right = next(false, 0, arr, 0, false);
  let left = next(true, 0, arr, 0, false);

  let result = Math.min(left, right);

  return result;
}
/**
 * @param {boolean} reverse : 왼쪽으로 움직이는지 판단
 * @param {number} p : 이번에 검사할 문자의 위치
 * @param {Array} arr : 검사중인 배열
 * @param {number} count : 조이스틱을 움직인 횟수
 * @param {boolean} canGoBack : 현재 진행방향과 반대 방향으로 움직일수 있는지 ( 이전 문자가 A였으면 true )
 */
const next = (reverse, p, arr, count, canGoBack) => {
  if (!hasNextStep(arr)) {
    return count - 1;
  }

  if (p === arr.length) { // 오른쪽 끝을 넘어간 경우
    p = 0;
  }
  if (p < 0) {            // 왼쪽 끝을 넘어간 경우 
    p = arr.length - 1;
  }

  let newArr = [...arr];
  let increase = charSelect(newArr[p]);
  newArr.splice(p, 1, "A");
    
  if (reverse) {  // 왼쪽으로 진행중일 경우
    if (increase !== 0) { // 현재 문자가 A가 아닌
      let left = next(reverse, p - 1, newArr, count + increase + 1, true);
      return left;
    }
    if (!canGoBack) { // 현재 문자가 A이지만, 반대방향으로 진행할 수 없는 경우
      let left = next(reverse, p - 1, newArr, count + 1, false);
      return left;
    }

    let left = next(reverse, p - 1, newArr, count + 1, false);
    let right = next(!reverse, p + 1, newArr, count - 1, false);
    return Math.min(left, right);
    
  } else {        // 오른쪽으로 진행중일 경우
    if (increase !== 0) {
      let right = next(reverse, p + 1, newArr, count + increase + 1, true);
      return right;
    }
    if (!canGoBack) {
      let right = next(reverse, p + 1, newArr, count + 1, false);
      return right;
    }

    let left = next(!reverse, p - 1, newArr, count - 1, false);
    let right = next(reverse, p + 1, newArr, count + 1, false);
    return Math.min(left, right);
  }
};

const charSelect = (char) => {
  let charCode = char.charCodeAt();
  return Math.min(charCode - 65, 91 - charCode);
};

const hasNextStep = (arr) => {
  return arr.join("").replace(/A/gi, "").length > 0;
};

깔끔하게 만들 수 있겠는데?

next() 함수의 파라미터가 많기도 하고,

분기문 안쪽이 너무 보기 싫어서 좀 깔끔하게 수정해보려는 찰나

포인터를 왔다갔다 할 필요 없이 다음 위치를 0번 인덱스로 하는

새로운 String을 다음 시행으로 넘겨주면 되지 않나 라는 생각이 들었습니다


세번째 시도(성공)

구상

0. 모든 문자가 A인 경우 0을 반환하고 종료합니다

1. 양방향으로 분기합니다

2. 우측으로 진행하며 문자열을 변경합니다

3. A를 만나면 분기합니다

역방향으로 진행할 경우 한번 되돌아가는 것이므로

count -= 1 이 필요합니다

3-1. 이전 수행에서의 문자가 A인 경우 진행방향을 유지합니다

이전 수행에서의 원본 문자가 A인 경우 진행방향을 유지합니다

4. 전부 A가 되면 조이스틱을 움직인 횟수를 반환합니다

코드

function solution(name) {
  if (!hasNextStep(name)) {
    return 0;
  }

  let arr = name.split("");
  
  let rightStr = name;
  let leftStr = arr[0] + arr.slice(1, arr.length).reverse().join("");

  let right = next(rightStr, 0, false);
  let left = next(leftStr, 0, false);

  let result = Math.min(left, right);

  return result;
}
const next = (str, count, canGoBack) => {
  if (!hasNextStep(str)) {
    return count - 1;
  }

  let arr = str.split("");
  let increase = charSelect(arr[0]);
  let nextStr = {
    right: arr.slice(1, arr.length).join("") + "A",
    left: arr.reverse().join(""),
  };

  if (str[0] !== "A") {
    return next(nextStr["right"], count + increase + 1, true);
  }

  if (!canGoBack) {
    return next(nextStr["right"], count + increase + 1, false);
  }

  let right = next(nextStr["right"], count + increase + 1, false);
  let left = next(nextStr["left"], count + increase - 1, false);

  return Math.min(left, right);
};

const charSelect = (char) => {
  let charCode = char.charCodeAt();
  return Math.min(charCode - 65, 91 - charCode);
};

const hasNextStep = (str) => {
  let arr = str.split("");
  return arr.join("").replace(/A/gi, "").length > 0;
};

0개의 댓글