Skip to content

Commit cdce205

Browse files
[BAEL-8922] Generate a Valid Expression From a String of Numbers to Get Target Number (#18469)
- rename unit test; add additional test cases
1 parent 7eb8b7f commit cdce205

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package com.baeldung.backtracking;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class Backtracking {
7+
8+
private Backtracking() {
9+
}
10+
11+
public static List<String> process(String digits, int target) {
12+
final List<String> validExpressions = new ArrayList<>();
13+
evaluateExpressions(validExpressions, new Equation(digits, target), 0, new StringBuilder(), 0, 0);
14+
return validExpressions;
15+
}
16+
17+
private static void evaluateExpressions(List<String> validExpressions, Equation equation, int index, StringBuilder currentExpression, long currentResult,
18+
long lastOperand) {
19+
20+
if (allDigitsProcessed(equation.getDigits(), index)) {
21+
if (currentResult == equation.getTarget()) {
22+
validExpressions.add(currentExpression.toString());
23+
}
24+
return;
25+
}
26+
27+
exploreExpressions(validExpressions, equation, index, currentExpression, currentResult, lastOperand);
28+
}
29+
30+
private static boolean allDigitsProcessed(String digits, int index) {
31+
return index == digits.length();
32+
}
33+
34+
private static void exploreExpressions(List<String> validExpressions, Equation equation, int index, StringBuilder currentExpression, long currentResult,
35+
long lastOperand) {
36+
37+
for (int endIndex = index; endIndex < equation.getDigits()
38+
.length(); endIndex++) {
39+
if (isWithLeadingZero(equation.getDigits(), index, endIndex)) {
40+
break;
41+
}
42+
43+
long currentOperandValue = Long.parseLong(equation.getDigits()
44+
.substring(index, endIndex + 1));
45+
46+
if (isFirstOperand(index)) {
47+
processFirstOperand(validExpressions, equation, endIndex, currentExpression, currentOperandValue);
48+
} else {
49+
applyAddition(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue);
50+
applySubtraction(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue);
51+
applyMultiplication(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue, lastOperand);
52+
}
53+
}
54+
}
55+
56+
private static boolean isWithLeadingZero(String digits, int index, int endIndex) {
57+
return endIndex > index && digits.charAt(index) == '0';
58+
}
59+
60+
private static boolean isFirstOperand(int index) {
61+
return index == 0;
62+
}
63+
64+
private static void processFirstOperand(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression,
65+
long currentOperandValue) {
66+
appendToExpression(currentExpression, Operator.NONE, currentOperandValue);
67+
68+
evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentOperandValue, currentOperandValue);
69+
70+
removeFromExpression(currentExpression, Operator.NONE, currentOperandValue);
71+
}
72+
73+
private static void applyAddition(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
74+
long currentOperandValue) {
75+
appendToExpression(currentExpression, Operator.ADDITION, currentOperandValue);
76+
77+
evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult + currentOperandValue, currentOperandValue);
78+
79+
removeFromExpression(currentExpression, Operator.ADDITION, currentOperandValue);
80+
}
81+
82+
private static void applySubtraction(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
83+
long currentOperandValue) {
84+
appendToExpression(currentExpression, Operator.SUBTRACTION, currentOperandValue);
85+
86+
evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult - currentOperandValue, -currentOperandValue);
87+
88+
removeFromExpression(currentExpression, Operator.SUBTRACTION, currentOperandValue);
89+
}
90+
91+
private static void applyMultiplication(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
92+
long currentOperandValue, long lastOperand) {
93+
appendToExpression(currentExpression, Operator.MULTIPLICATION, currentOperandValue);
94+
95+
evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult - lastOperand + (lastOperand * currentOperandValue),
96+
lastOperand * currentOperandValue);
97+
98+
removeFromExpression(currentExpression, Operator.MULTIPLICATION, currentOperandValue);
99+
}
100+
101+
private static void appendToExpression(StringBuilder currentExpression, Operator operator, long currentOperand) {
102+
currentExpression.append(operator.getSymbol())
103+
.append(currentOperand);
104+
}
105+
106+
private static void removeFromExpression(StringBuilder currentExpression, Operator operator, long currentOperand) {
107+
currentExpression.setLength(currentExpression.length() - operator.getSymbolLength() - String.valueOf(currentOperand)
108+
.length());
109+
}
110+
111+
private enum Operator {
112+
ADDITION("+"),
113+
SUBTRACTION("-"),
114+
MULTIPLICATION("*"),
115+
NONE("");
116+
117+
private final String symbol;
118+
private final int symbolLength;
119+
120+
Operator(String symbol) {
121+
this.symbol = symbol;
122+
this.symbolLength = symbol.length();
123+
}
124+
125+
public String getSymbol() {
126+
return symbol;
127+
}
128+
129+
public int getSymbolLength() {
130+
return symbolLength;
131+
}
132+
}
133+
134+
private static class Equation {
135+
136+
private final String digits;
137+
private final int target;
138+
139+
public Equation(String digits, int target) {
140+
this.digits = digits;
141+
this.target = target;
142+
}
143+
144+
public String getDigits() {
145+
return digits;
146+
}
147+
148+
public int getTarget() {
149+
return target;
150+
}
151+
}
152+
153+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.baeldung.backtracking;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.Arrays;
7+
import java.util.Collections;
8+
import java.util.List;
9+
import java.util.stream.Stream;
10+
11+
import org.junit.jupiter.api.Assertions;
12+
import org.junit.jupiter.params.ParameterizedTest;
13+
import org.junit.jupiter.params.provider.Arguments;
14+
import org.junit.jupiter.params.provider.MethodSource;
15+
16+
class BacktrackingUnitTest {
17+
18+
private static Stream<Arguments> equationsWithNoSolutions() {
19+
return Stream.of(Arguments.of("3456237490", 9191), Arguments.of("5", 0));
20+
}
21+
22+
@ParameterizedTest
23+
@MethodSource("equationsWithNoSolutions")
24+
void givenEquationsWithNoSolutions_whenProcess_thenEmptyListIsReturned(String digits, int target) {
25+
final List<String> result = Backtracking.process(digits, target);
26+
assertTrue(result.isEmpty());
27+
}
28+
29+
private static Stream<Arguments> equationsWithValidSolutions() {
30+
return Stream.of(Arguments.of("1", 1, Collections.singletonList("1")), Arguments.of("00", 0, Arrays.asList("0+0", "0-0", "0*0")),
31+
Arguments.of("123", 6, Arrays.asList("1+2+3", "1*2*3")), Arguments.of("232", 8, Arrays.asList("2*3+2", "2+3*2")),
32+
Arguments.of("534", -7, Collections.singletonList("5-3*4")), Arguments.of("1010", 20, Collections.singletonList("10+10")),
33+
Arguments.of("1234", 10, Arrays.asList("1+2+3+4", "1*2*3+4")), Arguments.of("1234", -10, Collections.singletonList("1*2-3*4")),
34+
Arguments.of("12345", 15, Arrays.asList("1+2+3+4+5", "1*2*3+4+5", "1-2*3+4*5", "1+23-4-5")));
35+
}
36+
37+
@ParameterizedTest
38+
@MethodSource("equationsWithValidSolutions")
39+
void givenEquationsWithValidSolutions_whenProcess_thenValidResultsAreReturned(String digits, int target, List<String> expectedSolutions) {
40+
final List<String> result = Backtracking.process(digits, target);
41+
42+
assertEquals(expectedSolutions.size(), result.size());
43+
44+
expectedSolutions.stream()
45+
.map(result::contains)
46+
.forEach(Assertions::assertTrue);
47+
}
48+
49+
}

0 commit comments

Comments
 (0)