처음엔 단순하게 생각해서 왼쪽, 오른쪽으로만 나눠서 진행해 봤습니다
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'와 같이 진행방향의 반대방향으로 돌아가야 하는 경우도 고려해야 합니다
다만 이 때 조이스틱이 움직인 횟수를 잘 생각해야 합니다
진행방향이 바뀌는 경우 3번 인덱스를 기준으로
조이스틱을 ( L ) 로만 움직여도 되는 걸 ( R L L ) 와 같이 움직여줬으므로
그만큼 count에서 다시 빼줘야 합니다
이전 수행에서의 원본 문자가 A인 경우 진행방향을 유지합니다
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을 다음 시행으로 넘겨주면 되지 않나 라는 생각이 들었습니다
역방향으로 진행할 경우 한번 되돌아가는 것이므로
count -= 1
이 필요합니다
이전 수행에서의 원본 문자가 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;
};