문제

https://programmers.co.kr/learn/courses/30/lessons/67257


코드

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
private static final char PLUS = '+';
private static final char MINUS = '-';
private static final char MULTIPLICATION = '*';

public long solution(String expression) {
char[] chars = expression.toCharArray();

Object[] numberAndOperator = getNumbersAndOperatorSet(chars);
List<Long> numberList = (List<Long>) numberAndOperator[0];
List<Character> operatorList = (List<Character>) numberAndOperator[1];

char[] operators = getExpressionIncludeOperators(operatorList);
List<String> operatorSetList = getOperatorSetList(operators);

return getMax(numberList, operatorList, operatorSetList);
}

private Object[] getNumbersAndOperatorSet(char[] expression) {
List<Long> numberList = new ArrayList<>();
List<Character> operatorList = new ArrayList<>();

StringBuilder sb = new StringBuilder();
for (char c : expression) {
switch (c) {
case PLUS:
case MINUS:
case MULTIPLICATION:
numberList.add(Long.valueOf(sb.toString()));
operatorList.add(c);

sb.setLength(0);
break;
default:
sb.append(c);
break;
}
}

numberList.add(Long.valueOf(sb.toString()));

return new Object[] {numberList, operatorList};
}

private char[] getExpressionIncludeOperators(List<Character> operatorList) {
Set<Character> set = new HashSet<>();
List<Character> list = new ArrayList<>();

for (char c : operatorList) {
if (set.add(c)) {
list.add(c);
}
}

char[] operators = new char[list.size()];
int i = 0;
for (char c : list) {
operators[i++] = c;
}

return operators;
}


private List<String> getOperatorSetList(char[] chars) {
List<String> operatorSetList = new ArrayList<>();
Set<String> set = new HashSet<>();

for (int i = 1; i <= chars.length; i++) {
perm(chars, 0, i, operatorSetList, set);
}

return operatorSetList;
}

private void perm(char[] array, int depth, int length, List<String> operatorList, Set<String> set) {
if (depth == length) {

StringBuilder sb = new StringBuilder();
for (char c : array) {
sb.append(c);
}

if (set.add(sb.toString())) {
operatorList.add(sb.toString());
}

return;
}

for (int i = depth; i < array.length; i++) {
swap(array, i, depth);
perm(array, depth + 1, length, operatorList, set);
swap(array, i, depth);
}
}

private void swap(char[] arrary, int i, int j) {
char temp = arrary[i];
arrary[i] = arrary[j];
arrary[j] = temp;
}

private Long getMax(List<Long> numberList, List<Character> operatorList, List<String> operatorSetList) {
long max = 0;

for (String operatorSet : operatorSetList) {
char[] operators = operatorSet.toCharArray();

LinkedList<Long> numberQueue = new LinkedList<>(numberList);
LinkedList<Character> operatorQueue = new LinkedList<>(operatorList);

for (char operatorToCalculate : operators) {
Stack<Long> numberStack = new Stack<>();
Stack<Character> operatorStack = new Stack<>();

long first = numberQueue.poll();
numberStack.push(first);
numberQueue.offer(first);

Stack[] stacks = new Stack[] {numberStack, operatorStack};
LinkedList[] queues = new LinkedList[] {numberQueue, operatorQueue};

calculatePriority(stacks, queues, operatorToCalculate);
}

max = (max > Math.abs(numberQueue.peek())) ? max : Math.abs(numberQueue.poll());
}

return max;
}

private void calculatePriority(Stack[] staks, LinkedList[] queues, char operatorToCalculate) {
Stack<Long> numberStack = staks[0];
Stack<Character> operatorStack = staks[1];

LinkedList<Long> numberQueue = queues[0];
LinkedList<Character> operatorQueue = queues[1];

int limit = operatorQueue.size();
for (int i = 0; i < limit; i++) {
long currentNumber = numberQueue.poll();
numberStack.push(currentNumber);

Character currentOperator = operatorQueue.poll();
operatorStack.push(currentOperator);

if (operatorStack.peek() == operatorToCalculate) {
long n1 = numberStack.pop();
long n2 = numberStack.pop();
char op = operatorStack.pop();

long sum = sum(n2, n1, op);
numberStack.push(sum);

numberQueue.pollLast();
numberQueue.offer(sum);
} else {
numberQueue.offer(currentNumber);
operatorQueue.offer(currentOperator);
}
}
}

private Long sum(long n2, long n1, char op) {
long sum = 0;

switch (op) {
case '*' :
sum = n2 * n1;
break;
case '+' :
sum = n2 + n1;
break;
case '-' :
sum = n2 - n1;
break;
}

return sum;
}

흐름

  1. 수식으로 넘어온 문자열을 숫자와 연산자로 나눈다.
  2. 연산자들을 중복되지 않게 저장한다.
  3. 문자열에 포함되어 있는 연산자들을 재귀로 반복하면서 모든 순열을 구한다.
    1. 0번째와 1번째를 교환하고 0번째와 2번째를 교환하고 하는 식으로 모든 순열을 구한다.
    2. 연산자의 개수만큼 순열로 만들어서 경우의 수를 구하기 위해 연산자가 2개인 경우엔 2! = 2개, 3개인 경우엔 3! = 6개가 된다.
  4. 모든 경우의 수를 구했으면 우선 순열의 수 만큼 반복해서 모든 경우의 수의 값을 구해야 하므로 순열의 수 만큼 반복한다.
  5. 돌면서 숫자를 담은 리스트와 연산자를 담은 리스트의 사이즈와 같은 큐를 만들고 각각 값들을 저장해서 초기화한다.
  6. 이번에 계산 할 순열(ex) * + -)을 char[]로 나눠서 그 갯수만큼 loop 돈다.
  7. 돌면서 숫자와 연산자를 저장 할 Stack를 각각 만든다.
  8. 숫자를 저장한 큐에서 하나를 꺼내서 stack에 저장하고 꺼낸 놈을 다시 큐의 맨 마지막에 저장한다.
  9. 그 후 큐의 개수만큼 loop 돌면서 큐들에서 하나씩 꺼내고 마찬가지로 스택에 저장한다.
  10. 그리고 꺼낸 연산자가 지금 계산 해야할 연산자라면 stack들에서 숫자 2개와 연산자를 pop하고 연산자에 맞게 계산해준다.
  11. 계산 한 값은 뒤에 또 그 값을 다른 값과 계산해야하므로 stack와 queue에 저장한다.
  12. 그리고 8번에서 큐에 초기값을 먼저 저장해서 큐의 맨 앞값과 큐의 맨 마지막 값이 계산된 것이므로 큐의 맨 마지막 값을 꺼낸다.
  13. 이번에 계산 할 연산자가 아니라면 지금 꺼낸 값과 연산자는 다시 큐에 저장한다.
  14. 4번부터 13번 까지를 모두 반복하면 끝

* + - 일 경우 Queue와 Stack의 변화

  1. 초기화
1
2
LinkedList<Long> numberQueue = new LinkedList<>(numberList);
LinkedList<Character> operatorQueue = new LinkedList<>(operatorList);
1 2 3 4 5
숫자 100 200 300 500 20
연산자 - * - +
  1. 숫자 초기값 저장
1
2
3
long first = numberQueue.poll();
numberStack.push(first);
numberQueue.offer(first);
1 2 3 4 5
숫자 200 300 500 20 100
연산자 - * - +

숫자 연산자
5
4
3
2
1 100
  1. 이후 반복
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
for (int i = 0; i < limit; i++) {
long currentNumber = numberQueue.poll();
numberStack.push(currentNumber);

Character currentOperator = operatorQueue.poll();
operatorStack.push(currentOperator);

if (operatorStack.peek() == operatorToCalculate) {
long n1 = numberStack.pop();
long n2 = numberStack.pop();
char op = operatorStack.pop();

long sum = sum(n2, n1, op);
numberStack.push(sum);

numberQueue.pollLast();
numberQueue.offer(sum);
} else {
numberQueue.offer(currentNumber);
operatorQueue.offer(currentOperator);
}
}

1회 반복

1 2 3 4 5
숫자 300 500 20 100 200
연산자 * - + -
숫자 연산자
5
4
3
2 200
1 100 -

2회 반복

1 2 3 4 5
숫자 500 20 100 60000
연산자 - + -
숫자 연산자
5
4
3
2 60000
1 100

3회 반복

1 2 3 4 5
숫자 20 100 60000 500
연산자 + - -
숫자 연산자
5
4
3 500
2 60000
1 100 -

4회 반복

1 2 3 4 5
숫자 100 60000 500 20
연산자 - - +
숫자 연산자
5
4 20
3 500 -
2 60000 +
1 100 -
  • 이런 식으로 반복되고 queue 크기 만큼 돌았다는건 우선 계산해야 될 연산자를 다 계산 했다는 것이므로 다시 스택을 비우고 처음부터 위 계산을 반복한다.
  • 다시 계산해도 큐에 계산 된 값이 저장되어 있으므로 정상적으로 계산된다.

결과

번호 속도
테스트 1 통과 (1.22ms, 52.9MB)
테스트 2 통과 (1.25ms, 51.9MB)
테스트 3 통과 (1.54ms, 51.7MB)
테스트 4 통과 (1.62ms, 52.6MB)
테스트 5 통과 (1.70ms, 52.8MB)
테스트 6 통과 (1.73ms, 52.9MB)
테스트 7 통과 (1.69ms, 52.1MB)
테스트 8 통과 (1.76ms, 52.9MB)
테스트 9 통과 (1.81ms, 52.3MB)
테스트 10 통과 (1.86ms, 52.1MB)
테스트 11 통과 (1.70ms, 50.6MB)
테스트 12 통과 (1.90ms, 52.3MB)
테스트 13 통과 (2.62ms, 52.2MB)
테스트 14 통과 (2.17ms, 53.2MB)
테스트 15 통과 (2.15ms, 53.1MB)
테스트 16 통과 (1.36ms, 52.7MB)
테스트 17 통과 (1.57ms, 52.8MB)
테스트 18 통과 (1.28ms, 52.9MB)
테스트 19 통과 (1.30ms, 51.3MB)
테스트 20 통과 (1.33ms, 52.6MB)
테스트 21 통과 (1.53ms, 52.5MB)
테스트 22 통과 (1.47ms, 54.3MB)
테스트 23 통과 (1.21ms, 52.3MB)
테스트 24 통과 (2.11ms, 50.7MB)
테스트 25 통과 (2.17ms, 52.2MB)
테스트 26 통과 (1.39ms, 52.3MB)
테스트 27 통과 (2.70ms, 53MB)
테스트 28 통과 (1.74ms, 52.6MB)
테스트 29 통과 (1.59ms, 50.4MB)
테스트 30 통과 (1.58ms, 50.2MB)

테스트 케이스

1
2
assertEquals(60420, test.solution("100-200*300-500+20"));
assertEquals(300, test.solution("50*6-3*2"));