문제
조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다.ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA
조이스틱을 각 방향으로 움직이면 아래와 같습니다.
▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로)
◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서)
▶ - 커서를 오른쪽으로 이동 (마지막 위치에서 오른쪽으로 이동하면 첫 번째 문자에 커서)
예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.
• 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다.
• 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다.
따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.
제한조건
- name은 알파벳 대문자로만 이루어져 있습니다.
- name의 길이는 1 이상 20 이하입니다.
입출력 예시
name | return |
"JEROEN" | 56 |
"JAN" | 23 |
C#
최소 이동하는 방법을 찾아야 해.
- A를 기준으로 N보다 뒤에 있는 알파벳은 뒤로 이동이 최소 거리임.
- 알파벳이 대문자로 이뤄져 있으니 아스키 코드 변환 시 65 ~ 90임
- N = 78 → 78 - 65 = 13 → 이동횟수는 A ~ N = 알파벳-65 / O ~ Z = 알파벳-65 % 27 + 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
public class Solution
{
public int solution(string name)
{
int stdNum = 65;
char[] alphabet = name.ToCharArray();
int curPoint = 0, direction = 0;
int moveCnt = CheckedMove(alphabet[curPoint]);
if (alphabet.Length == 1)
return moveCnt;
if(alphabet[1] == 'A')
{
curPoint = alphabet.Length - 1;
direction = -1;
}
else
{
curPoint = 1;
direction = 1;
}
do
{
moveCnt += CheckedMove(alphabet[curPoint]) + 1;
curPoint = (curPoint + direction) % alphabet.Length;
}
while (curPoint != 0);
return moveCnt;
}
private int CheckedMove(char alphabet)
{
int charNum = Convert.ToInt32(alphabet) - 65;
if (charNum > 78) // N = 78
{
charNum = charNum % 26 + 1;
}
return charNum;
}
}
[실패]
- 남은 데이터가 전부 A만 남은 경우에는 이동하지 않고 종료해야 하는데, 이 부분을 고려하지 않음
- Z부터 거꾸로 이동하는 횟수 계산 방법이 틀림
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Linq;
public class Solution
{
public int solution(string name)
{
char[] alphabet = name.ToCharArray();
int curPoint = 0, direction = 1;
int moveCnt = CheckedMove(alphabet[curPoint++]);
if (alphabet.Length == 1)
return moveCnt;
if(alphabet[1] == 'A')
{
curPoint = alphabet.Length - 1;
direction = -1;
}
do
{
moveCnt += CheckedMove(alphabet[curPoint]) + 1;
curPoint = (curPoint + direction) % alphabet.Length;
if (direction > 0)
{
if (name.Substring(curPoint, name.Length - curPoint).Any(x => x != 'A') == false)
{
return moveCnt;
}
}
else
{
if (name.Substring(1, curPoint).Any(x => x != 'A') == false)
{
return moveCnt;
}
}
}
while (curPoint != 0);
return moveCnt;
}
private int CheckedMove(char alphabet)
{
int charNum = Convert.ToInt32(alphabet) - 65;
if (charNum > 13) // N = 78
{
charNum = 26 - charNum;
}
return charNum;
}
}
[실패]
예외 사항을 고려하지 못함 JAM일 때는 뒤로 돌아서 시작하는 게 유리하지만,
JMAAAAAAACD이라면? 왼쪽으로 순차적으로 이동할 시 이동 횟수가 10이 되지만, 왼쪽으로 JM만큼 이동했다가 다시 되돌아 와서 뒤에서 부터 이동하는게 훨씬 효율적임. (J > M > J D C → 이동 횟수 5회)
예외 사항을 고려해 다시 시나리오 작성
- 연속된 A가 가장 많은 곳 위치를 찾음
- 찾은 위치를 기준으로 Left와 Right를 나눔
- 다음과 같은 상황 중 가장 이동횟수가 적은 상황을 선택함
- 왼쪽부터 오른쪽으로 차례대로 이동
- 맨 뒤로 이동해서 오른쪽에서 왼쪽으로 차례대로 이동
- 시작 위치에서 오른쪽으로 가다가 A 만나면 다시 되돌아 가서 가장 마지막으로 이동 후 A를 만날 때 까지 이동
- 맨 뒤로 이동 후 왼쪽으로 가다가 A를 만나면 시작 위치로 이동 후 시작 점에서 A를 만날 때까지 이동
- 계산된 이동 횟수가 처음부터 끝까지 이동하는 횟수나 또는 마지막에서 처음으로 이동하는 횟수보다 작은지 비교
- 계산된 이동 횟수가 작은 경우, 계산된 이동 횟수 + 알파벳 변환 수 반환
- 처음부터 끝까지 이동하는 값이 가장 작은 경우, name.Length - 1 + 알파벳 변환 수 반환
- 거꾸로 이동하는 값이 가장 작은 경우, name.Length + 알파벳 변환 수 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using System;
using System.Linq;
using System.Text.RegularExpressions;
public class Solution
{
public int solution(string name)
{
//A가 가장 많이 모여 있는 곳의 위치를 찾음
var chkVals = Regex.Split(name, @"[^A]");
var maxA = chkVals.OrderByDescending(x => x.Length).Take(1).FirstOrDefault();
if (string.IsNullOrWhiteSpace(maxA)) //A없음 -> 차례대로 진행
{
return CountMove(name.ToCharArray()) + name.Length - 1;
}
var splitName = name.Split(maxA);
if (splitName[0].Trim() == "")
{
string chkStr = string.Join("", splitName.Skip(1));
return CountMove(chkStr.ToCharArray()) + chkStr.Length;
}
else if (splitName[splitName.Length - 1].Trim() == "")
{
string chkStr = string.Join("", splitName.Take(splitName.Length - 1));
return CountMove(chkStr.ToCharArray()) + chkStr.Length - 1;
}
int left = splitName[0].Length - 1;
int right = splitName[splitName.Length-1].Length - 1;
int moveLeft = left * 2 + right + 1;
int moveRight = right * 2 + left + 2;
int moveMid = 0;
int cntA = 1;
if(splitName.Length > 2)
{
string cleanName = name.Replace(maxA, "");
cntA = (name.Length - cleanName.Length) / maxA.Length;
moveLeft += (cntA - 1) * maxA.Length;
moveRight += (cntA - 1) * maxA.Length;
string midStr = string.Join("", splitName.Skip(1).Take(splitName.Length - 2));
moveMid = CountMove(midStr.ToCharArray());
}
int minPath = moveLeft > moveRight ? moveRight : moveLeft;
int moveCnt = 0;
if (chkVals[0] != "" && chkVals[0] != maxA)
{
if(name.Length - chkVals[0].Length < minPath + moveMid)
{
moveCnt = CountMove(name.Substring(chkVals.Length).ToCharArray());
return moveCnt + name.Length - chkVals[0].Length;
}
}
else if (chkVals[chkVals.Length - 1] != "" && chkVals[chkVals.Length - 1] != maxA)
{
int len = chkVals.Length - 1;
if (name.Length - chkVals[len].Length - 1 < minPath + moveMid)
{
moveCnt = CountMove(name.Substring(0, name.Length - len).ToCharArray());
return moveCnt + name.Length - chkVals[len].Length - 1;
}
}
else if (minPath + moveMid > name.Length - 1)
{
return CountMove(name.ToCharArray()) + name.Length - 1;
}
else if(minPath + moveMid > name.Length)
{
return CountMove(name.ToCharArray()) + name.Length;
}
string remainStr = "";
if(moveLeft < moveRight)
{
moveCnt = CountMove(splitName[0].ToCharArray());
remainStr = string.Join("", splitName.Skip(1));
}
else
{
int max = splitName.Length - 1;
moveCnt = CountMove(splitName[max].ToCharArray());
remainStr = string.Join("", splitName.Take(max));
}
moveCnt += CountMove(remainStr.ToArray());
return moveCnt + minPath + moveMid;
}
private int CheckedMove(char alphabet)
{
int charNum = Convert.ToInt32(alphabet) - 65;
if (charNum > 13) // N = 78
{
charNum = 26 - charNum;
}
return charNum;
}
private int CountMove(char[] alphabet)
{
int cnt = 0;
foreach (char item in alphabet)
{
cnt += CheckedMove(item);
}
return cnt;
}
}
[실패] ⇒ 테스트 케이스 3, 4, 7, 12 실패
모든 케이스를 다 확인했다고 생각했는데 오류가 나길래 테스트 케이스를 좀 찾아봤다. 찾아본 케이스 중 다른 결과값이 나오는 케이스를 찾았다.
name | 기대값 | 실행 결과 |
---|---|---|
AABAA | 3 | 2 |
ABABB | 7 | 6 |
연속된 A개수를 가장 많이 가진 A그룹이 1개를 초과하면서, 연속된 A를 가장 많이 가직 있는 그룹으로 시작하거나 끝나는 경우에 대해 예외처리를 해주지 않았음 → 연속된 A개수를 가장 많이 가진 A그룹의 개수를 확인하여 1개를 초과한 경우 A길이만큼 더해 줌 → 중복된 그룹에 대한 이동 횟수를 포함할 수 있음!
[미해결]
프로그래머스에서 문제 확인