package com.freetymekiyan.algorithms.level.medium;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* Given a nested list of integers represented as a string, implement a parser to deserialize it.
* <p>
* Each element is either an integer, or a list -- whose elements may also be integers or other lists.
* <p>
* Note: You may assume that the string is well-formed:
* <p>
* String is non-empty.
* String does not contain white spaces.
* String contains only digits 0-9, [, -, ,, ].
* <p>
* Example 1:
* <p>
* Given s = "324",
* <p>
* You should return a NestedInteger object which contains a single integer 324.
* Example 2:
* <p>
* Given s = "[123,[456,[789]]]",
* <p>
* Return a NestedInteger object containing a nested list with 2 elements:
* <p>
* 1. An integer containing value 123.
* 2. A nested list containing two elements:
* i. An integer containing value 456.
* ii. A nested list with one element:
* a. An integer containing value 789.
* <p>
* Tags: Stack, String
* Similar Problems: (M) Flatten Nested List Iterator
*/
public class MiniParser {
private MiniParser m;
public NestedInteger deserialize(String s) {
if (!s.startsWith("[")) {
return new NestedInteger(Integer.parseInt(s));
}
Deque<NestedInteger> stack = new ArrayDeque<>();
NestedInteger res = null;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '[') {
stack.push(new NestedInteger());
} else if (c == '-' || Character.isDigit(c)) {
int j = i + 1;
while (j < s.length() && Character.isDigit(s.charAt(j))) {
j++;
}
int val = Integer.parseInt(s.substring(i, j));
NestedInteger ni = new NestedInteger(val);
stack.peek().add(ni);
i = j - 1;
} else if (c == ']') {
res = stack.pop();
if (!stack.isEmpty()) {
stack.peek().add(res); // Add to previous NestedInteger's list
}
} // if c is ',', skip it
}
return res;
}
@Before
public void setUp() {
m = new MiniParser();
}
@Test
public void testExamples() {
String input = "[456,[789]]";
Assert.assertEquals(input, m.deserialize(input).toString());
input = "[123,[456,[789]]]";
Assert.assertEquals(input, m.deserialize(input).toString());
input = "324";
Assert.assertEquals(input, m.deserialize(input).toString());
input = "-3";
Assert.assertEquals(input, m.deserialize(input).toString());
}
@After
public void tearDown() {
m = null;
}
// This is the interface that allows for creating nested lists.
// You should not implement it, or speculate about its implementation
class NestedInteger {
private int value;
private boolean isInteger;
private ArrayList<NestedInteger> list;
// Constructor initializes an empty nested list.
public NestedInteger() {
list = new ArrayList<>();
}
// Constructor initializes a single integer.
public NestedInteger(int value) {
this.value = value;
isInteger = true;
}
// @return true if this NestedInteger holds a single integer, rather than a nested list.
public boolean isInteger() {
return isInteger;
}
// @return the single integer that this NestedInteger holds, if it holds a single integer
// Return null if this NestedInteger holds a nested list
public Integer getInteger() {
return isInteger ? value : null;
}
// Set this NestedInteger to hold a single integer.
public void setInteger(int value) {
this.value = value;
}
// Set this NestedInteger to hold a nested list and adds a nested integer to it.
public void add(NestedInteger ni) {
list.add(ni);
}
// @return the nested list that this NestedInteger holds, if it holds a nested list
// Return null if this NestedInteger holds a single integer
public List<NestedInteger> getList() {
return isInteger ? null : list;
}
@Override
public String toString() {
if (isInteger) {
return Integer.toString(value);
}
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < list.size(); i++) {
sb.append(list.get(i).toString());
if (i < list.size() - 1) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
}
}