Error in servlet url pattern matching implementation - java

I'm implementing the Servlet URL pattern matching follow the Servlet Specification. My matching method:
public static boolean match(String pattern, String str, boolean isCaseSensitive) {
char[] patArr = pattern.toCharArray();
char[] strArr = str.toCharArray();
int patIdxStart = 0;
int patIdxEnd = patArr.length - 1;
int strIdxStart = 0;
int strIdxEnd = strArr.length - 1;
boolean containsStar = false;
for (int i = 0; i < patArr.length; i++) {
if (patArr[i] != '*') {
continue;
}
containsStar = true;
break;
}
if (!containsStar) {
if (patIdxEnd != strIdxEnd) {
return false;
}
for (int i = 0; i <= patIdxEnd; i++) {
char ch = patArr[i];
if (ch == '?')
continue;
if ((isCaseSensitive) && (ch != strArr[i])) {
return false;
}
if ((!isCaseSensitive)
&& (Character.toUpperCase(ch) != Character
.toUpperCase(strArr[i]))) {
return false;
}
}
return true;
}
if (patIdxEnd == 0) {
return true;
}
char ch;
while (((ch = patArr[patIdxStart]) != '*')
&& (strIdxStart <= strIdxEnd)) {
if (ch != '?') {
if ((isCaseSensitive) && (ch != strArr[strIdxStart])) {
return false;
}
if ((!isCaseSensitive)
&& (Character.toUpperCase(ch) != Character
.toUpperCase(strArr[strIdxStart]))) {
return false;
}
}
patIdxStart++;
strIdxStart++;
}
if (strIdxStart > strIdxEnd) {
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd)) {
if (ch != '?') {
if ((isCaseSensitive) && (ch != strArr[strIdxEnd])) {
return false;
}
if ((!isCaseSensitive)
&& (Character.toUpperCase(ch) != Character
.toUpperCase(strArr[strIdxEnd]))) {
return false;
}
}
patIdxEnd--;
strIdxEnd--;
}
if (strIdxStart > strIdxEnd) {
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
int patIdxTmp = -1;
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
continue;
}
patIdxTmp = i;
break;
}
if (patIdxTmp == patIdxStart + 1) {
patIdxStart++;
continue;
}
int patLength = patIdxTmp - patIdxStart - 1;
int strLength = strIdxEnd - strIdxStart + 1;
int foundIdx = -1;
for (int i = 0; i <= strLength - patLength; i++) {
int j = 0;
while (true)
if (j < patLength) {
ch = patArr[(patIdxStart + j + 1)];
if (ch != '?') {
if ((isCaseSensitive)
&& (ch != strArr[(strIdxStart + i + j)])) {
break;
}
if ((!isCaseSensitive)
&& (Character.toUpperCase(ch) != Character
.toUpperCase(strArr[(strIdxStart
+ i + j)])))
break;
} else {
j++;
continue;
}
} else {
foundIdx = strIdxStart + i;
break;
}
}
if (foundIdx == -1) {
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
But when I test with case below:
String pattern = "*.a*";
String path = "/index.abc";
String matches = match(pattern, path, true) ? "matches" : "unmatches";
System.out.println(path + " " + matches + " " + pattern);
The test case runs forever and cannot stop. I have 2 questions:
Is pattern "*.a*" valid with Servlet URL pattern matching spec?
How to fix this error to break the infinite loop?

Here is my Java Servlet Specification 3.1 (April 2013) Mapping Requests to Servlets implementation.
/**
* Java Servlet Specification 3.1 (April 2013)
* Mapping Requests to Servlets (Chapter 12) implementation.
*
* This class is thread safe.
*/
public class ServletMappingMatcher {
private final String[] patterns;
private final String[] welcomePages;
public ServletMappingMatcher(String... patterns) {
this(patterns, new String[0]);
}
public ServletMappingMatcher(String[] patterns, String[] welcomePages) {
this.patterns = patterns;
this.welcomePages = welcomePages;
}
public String getPatternForPath(String path) {
for (String pattern : patterns) {
if (matches(pattern, path)) {
return pattern;
}
}
return null;
}
private boolean matches(String pattern, String path) {
if (isPathMapping(pattern)) {
return pathMatches(pattern, path);
} else if (isExtensionPattern(pattern)) {
return extensionMatches(pattern, path);
} else if (isApplicationContextRoot(pattern)) {
return matchesApplicationContextRoot(path);
} else if (isDefaultServlet(pattern)) {
return matchesDefaultServlet(path);
}
return strictlyMatches(pattern, path);
}
private boolean isPathMapping(String pattern) {
return pattern.startsWith("/") && pattern.endsWith("/*");
}
private boolean isExtensionPattern(String pattern) {
return pattern.startsWith("*.");
}
private boolean isApplicationContextRoot(String pattern) {
return pattern.isEmpty();
}
private boolean isDefaultServlet(String pattern) {
return pattern.equals("/");
}
private boolean pathMatches(String pattern, String path) {
return path.startsWith(pattern.substring(0, pattern.length() - 1)) ||
path.equals(pattern.substring(0, pattern.length() - 2));
}
private boolean extensionMatches(String pattern, String path) {
return path.endsWith(pattern.substring(1));
}
private boolean matchesApplicationContextRoot(String path) {
return path.equals("/");
}
private boolean strictlyMatches(String pattern, String path) {
return path.equals(pattern);
}
private boolean matchesDefaultServlet(String path) {
if (path.endsWith("/")) {
return true;
}
for (String welcomePage : welcomePages) {
if (path.endsWith("/" + welcomePage)) {
return true;
}
}
return false;
}
}
And JUnit tests:
import org.junit.Assert;
import org.junit.Test;
public class ServletMappingMatcherTest {
#Test
public void testsFromSpec() {
final String servlet1Pattern = "/foo/bar/*";
final String servlet2Pattern = "/baz/*";
final String servlet3Pattern = "/catalog";
final String servlet4Pattern = "*.bop";
final String defaultServlet = "/";
final String[] patterns = {servlet1Pattern, servlet2Pattern, servlet3Pattern, servlet4Pattern, defaultServlet};
final String[] welcomePages = {"index.html"};
final ServletMappingMatcher matcher = new ServletMappingMatcher(patterns, welcomePages);
Assert.assertEquals(servlet1Pattern, matcher.getPatternForPath("/foo/bar/index.html"));
Assert.assertEquals(servlet1Pattern, matcher.getPatternForPath("/foo/bar/index.bop"));
Assert.assertEquals(servlet2Pattern, matcher.getPatternForPath("/baz"));
Assert.assertEquals(servlet2Pattern, matcher.getPatternForPath("/baz/index.html"));
Assert.assertEquals(servlet3Pattern, matcher.getPatternForPath("/catalog"));
Assert.assertEquals(defaultServlet, matcher.getPatternForPath("/catalog/index.html"));
Assert.assertEquals(servlet4Pattern, matcher.getPatternForPath("/catalog/rececar.bop"));
Assert.assertEquals(servlet4Pattern, matcher.getPatternForPath("/index.bop"));
}
}

I fixed it, in the while(true) loop add below line to break the loop:
[...]
while(true) {
if (ch != '?') {
if(...) {
//...
break;
}
if(...) {
//...
break;
}
j++; // Add this line to avoid infinite loop
}
}

Related

How do i properly add characters to string in recursion

So i have this function EDIT:Whole program as requested
//This is a java program to construct Expression Tree using Infix Expression
import java.io.*;
public class Infix_Expression_Tree
{
public static void main(String args[]) throws IOException
{
String result="";
File vhod = new File(args[0]);
try{
BufferedReader reader = new BufferedReader(new FileReader(vhod));
Tree t1 = new Tree();
String a = reader.readLine();
t1.insert(a);
t1.traverse(1,result);
System.out.println("rez "+ result);
ch = reader.readLine();
}catch(IOException e){
e.printStackTrace();
}
}
}
class Node
{
public char data;
public Node leftChild;
public Node rightChild;
public Node(char x)
{
data = x;
}
public void displayNode()
{
System.out.print(data);
}
}
class Stack1
{
private Node[] a;
private int top, m;
public Stack1(int max)
{
m = max;
a = new Node[m];
top = -1;
}
public void push(Node key)
{
a[++top] = key;
}
public Node pop()
{
return (a[top--]);
}
public boolean isEmpty()
{
return (top == -1);
}
}
class Stack2
{
private char[] a;
private int top, m;
public Stack2(int max)
{
m = max;
a = new char[m];
top = -1;
}
public void push(char key)
{
a[++top] = key;
}
public char pop()
{
return (a[top--]);
}
public boolean isEmpty()
{
return (top == -1);
}
}
class Conversion
{
private Stack2 s;
private String input;
private String output = "";
public Conversion(String str)
{
input = str;
s = new Stack2(str.length());
}
public String inToPost()
{
for (int i = 0; i < input.length(); i++)
{
char ch = input.charAt(i);
switch (ch)
{
case '+':
gotOperator(ch, 2);
break;
case '*':
gotOperator(ch,1);
break;
case '/':
gotOperator(ch, 3);
break;
case '(':
s.push(ch);
break;
case ')':
gotParenthesis();
//s.pop();
break;
default:
//gotOperator(ch, 0);
//break;
output = output + ch;
}
}
while (!s.isEmpty())
output = output + s.pop();
//System.out.println("to je output iz inToPost " +output);
return output;
}
private void gotOperator(char opThis, int prec1)
{
while (!s.isEmpty())
{
char opTop = s.pop();
if (opTop == '(')
{
s.push(opTop);
break;
} else
{
int prec2;
if (opTop == '+')
prec2 = 2;
else if(opTop=='*')
prec2=1;
else
prec2 = 3;
if (prec2 <= prec1)
{
s.push(opTop);
break;
} else
output = output + opTop;
}
}
s.push(opThis);
}
private void gotParenthesis()
{
while (!s.isEmpty())
{
char ch = s.pop();
if (ch == '(')
break;
else
output = output + ch;
}
}
}
class Tree
{
private Node root;
public Tree()
{
root = null;
}
public void insert(String s)
{
Conversion c = new Conversion(s);
s = c.inToPost();
Stack1 stk = new Stack1(s.length());
s = s + "#";
int i = 0;
char symbol = s.charAt(i);
Node newNode;
while (symbol != '#')
{
if (symbol >= '0' && symbol <= '9' || symbol >= 'A'
&& symbol <= 'Z' || symbol >= 'a' && symbol <= 'z')
{
newNode = new Node(symbol);
stk.push(newNode);
} else if (symbol == '+' || symbol == '/'
|| symbol == '*')
{
Node ptr1=null;
Node ptr2=null;
//if(!stk.isEmpty()){
ptr1 = stk.pop();
if(!stk.isEmpty()){
ptr2 = stk.pop();
}
//}
newNode = new Node(symbol);
newNode.leftChild = ptr2;
newNode.rightChild = ptr1;
stk.push(newNode);
}
/*else if(symbol=='/'){
Node ptr = stk.pop();
newNode = new Node(symbol);
newNode.leftChild = ptr;
newNode.rightChild=null;
stk.push(newNode);
}*/
symbol = s.charAt(++i);
}
root = stk.pop();
}
public void traverse(int type,String result)
{
System.out.println("Preorder Traversal:- ");
preOrder(root,result);
}
private void preOrder(Node localRoot, String result)
{
if(root==null){
return;
}
if (localRoot != null)
{
if(localRoot.data == '/'){
preOrder(localRoot.leftChild,result);
result=result + localRoot.data;
//StringBuilder stringBuilder1 = new StringBuilder();
//stringBuilder1.append(result).append(localRoot.data);
System.out.println(result);
//localRoot.displayNode();
preOrder(localRoot.rightChild,result);
return;
}else{
//System.out.println("trenutni root je" );
//localRoot.displayNode();
result=result + localRoot.data;
// StringBuilder stringBuilder1 = new StringBuilder();
//stringBuilder1.append(result).append(localRoot.data);
System.out.println(result);
preOrder(localRoot.leftChild,result);
//result=result + localRoot.data;
//System.out.print(root.data);
preOrder(localRoot.rightChild,result);
//System.out.print(root.data);
//preOrder(localRoot.rightChild);
return;
}
}
}
}
my problem with it is that with localRoot.DisplayNode() i get the result i want. But when i add the same thing to the String result it adds data, until i get to leaf of the tree(leaf doesnt have left/right child) so it returns to previous recursive call, and goes to right child, here somewhere the leaf(from which we came back) isnt in String anymore. How do i fix this?
String is defined in main method

Illegal Argument Exception in Java

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;
import static java.lang.System.in;
import static java.lang.System.out;
/*
*
*
Use a stack to check parentheses, balanced and nesting
* The parentheses are: (), [] and {}
*
* See:
* - UseAStack
*
*/
public class Ex3CheckParen {
public static void main(String[] args) {
new Ex3CheckParen().program();
}
void program() {
// All should be true
out.println(checkParentheses("()"));
out.println(checkParentheses("(()())"));
out.println(!checkParentheses("(()))")); // Unbalanced
out.println(!checkParentheses("((())")); // Unbalanced
out.println(checkParentheses("({})"));
out.println(!checkParentheses("({)}")); // Bad nesting
out.println(checkParentheses("({} [()] ({}))"));
out.println(!checkParentheses("({} [() ({)})")); // Unbalanced and bad nesting
}
// This is interesting because have to return, but what if no match?!?
boolean checkParentheses(String str) {
Deque<Character> stack = new ArrayDeque<>();
String k = "({[";
String s = ")]}";
for (int i = 0; i < str.length(); i++) {
if (k.contains(String.valueOf(str.charAt(i)))) {
stack.push(str.charAt(i));
} else if (s.contains(String.valueOf(str.charAt(i)))) {
if (matching(stack.peek()) == str.charAt(i)) { //ILLEGAL ARGUMENT EXCEPTION HERE
return true;
}
} else {
return false;
}
}
return false;
}
char matching(char ch) {
//char c = must initialize but to what?!
switch (ch) {
case ')':
return '('; // c = '('
case ']':
return '[';
case '}':
return '{';
default:
// return c;
throw new IllegalArgumentException("No match found");
}
}
}
I'm getting an exception error in the if statement containing matching. Unable to figure out the cause.
Maybe something like this?
public class Ex3CheckParen {
public static void main(String[] args) {
new Ex3CheckParen().program();
}
void program() {
// All should be true
out.println(checkParentheses("()"));
out.println(checkParentheses("(()())"));
out.println(!checkParentheses("(()))")); // Unbalanced
out.println(!checkParentheses("((())")); // Unbalanced
out.println(checkParentheses("({})"));
out.println(!checkParentheses("({)}")); // Bad nesting
out.println(checkParentheses("({} [()] ({}))"));
out.println(!checkParentheses("({} [() ({)})")); // Unbalanced and bad nesting
}
// This is interesting because have to return, but what if no match?!?
boolean checkParentheses(String str) {
Deque<Character> stack = new ArrayDeque<>();
String k = "({[";
String s = ")]}";
for (int i = 0; i < str.length(); i++) {
if (k.contains(String.valueOf(str.charAt(i)))) {
stack.push(str.charAt(i));
} else if (s.contains(String.valueOf(str.charAt(i)))) {
if (matching(stack.peek(), str.charAt(i))) {
return true;
}
} else {
return false;
}
}
return false;
}
boolean matching(char ch1, char ch2) {
if ('(' == ch1 && ch2 == ')' || '[' == ch1 && ch2 == ']' || '{' == ch1 && ch2 == '}') {
return true;
}
return false;
}
}
In my opinion by the way, the method checkParentheses(String str) should look more like this:
boolean checkParentheses(String str) {
Deque<Character> stack = new ArrayDeque<>();
String open = "({[";
String close = ")]}";
int length = 0;
for (int i = 0; i < str.length(); i++) {
char currentChar = str.charAt(i);
if (open.contains(String.valueOf(currentChar))) {
stack.push(currentChar);
length++;
} else if (close.contains(String.valueOf(currentChar))) {
if (!stack.isEmpty() && matching(stack.peek(), currentChar)) {
stack.pop();
length--;
}
else {
return false;
}
} else {
return false;
}
}
if (length == 0)
return true;
return false;
}
But it is totally up to you...

Java: Phone Number filter and allocation to "Elite"

Task is to allocate from bulk of numbers (200k total) Elite and Premium ones. Elite means number is very beautiful and expensive, Premium means more-less beautiful.
My solution works, but it is very slow. To process 200k numbers takes about 40 minutes! Problem is that I have to generate thousands of Regex patterns using masks and then process thousands of numbers through thousands of patterns!
Patterns looks like patternX, patternXY, patternAB, patternABC, patternXAB, patternXABC, patternXYZ, patternXYAB, patternXYAB, for example:
super.patternXYZ = "^\\d+XXYYZZ$|^\\d+ZZXYXY$|^\\d+YXXYYZZ..$";
super.patternXYAB = "^\\d+ABXXYY$|^\\d+ABXYXY$";
Where all the letters are represent mask of numbers: XXYY mathces 4488 or 9933 (X<>=Y) and AABB matches serial sequences like 3344 or 7788 (A+1=B)
Matching occurs by following:
#Override
public Set<String> performCollect() {
for (String number : numbers) {
if (isPatternXMatches(number)) {
result.add(number);
} else if (isPatternXYMatches(number)) {
result.add(number);
}
...
}
return result;
}
Where Regex patterns are being generated for every single match and match performs:
protected boolean isPatternXYZMatches(String number) {
for (int X = 0; X < 10; X++) {
for (int Y = 0; Y < 10; Y++) {
for (int Z = 0; Z < 10; Z++) {
Pattern pattern = Pattern.compile(patternXYZ.replace("X", String.valueOf(X)).replace("Y", String.valueOf(Y)).replace("Z", String.valueOf(Z)));
Matcher matcher = pattern.matcher(number);
if (matcher.find()) {
return true;
}
}
}
}
return false;
}
protected boolean isPatternXYABMatches(String number) {
for (int X = 0; X < 10; X++) {
for (int Y = 0; Y < 10; Y++) {
for (int A = 0, B = 1; B < 10; A++, B++) {
Pattern pattern = Pattern.compile(patternXYAB.replace("A", String.valueOf(A)).replace("B", String.valueOf(B)).replace("X", String.valueOf(X)).replace("Y", String.valueOf(Y)));
Matcher matcher = pattern.matcher(number);
if (matcher.find()) {
return true;
}
}
}
}
return false;
}
Question: Does anyone know or could suggest some better and faster solution?
I replaced Regex with custom matcher and 200k numbers are now processed during 5 seconds instead of 40 minutes!
public Set<String> performCollect() {
for (String number : numbers) {
if (isNumberMatches(number)) {
result.add(number);
}
}
return result;
}
protected boolean isNumberMatches(String number) {
NumberMatcher nm = new NumberMatcher(number, offset);
for (NumberPattern pattern : patterns) {
if (nm.processMatch(pattern)) {
return true;
}
}
return false;
}
...
public class NumberPattern {
private char[] maskChars;
private Integer weight;
public NumberPattern(String mask, Integer weight) {
maskChars = mask.toCharArray();
this.weight = weight;
}
public char[] getMaskChars() {
return maskChars;
}
public void setMaskChars(char[] maskChars) {
this.maskChars = maskChars;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
...
public class NumberMatcher {
private char[] numberChars;
private int uniqueChars = 0;
public NumberMatcher(String number, int offset) {
numberChars = number.toCharArray();
List<Character> chars = new ArrayList<>();
for (Character ch : number.substring(offset).toCharArray()) {
if (!chars.contains(ch)) {
uniqueChars++;
chars.add(ch);
}
}
}
public boolean processMatch(NumberPattern pattern) {
if (pattern.getWeight() < uniqueChars) {
return false;
}
Character X = null;
Character Y = null;
Character Z = null;
Character A = null;
Character B = null;
Character C = null;
Character D = null;
final char[] patternChars = pattern.getMaskChars();
int patternIndex = patternChars.length;
int numberIndex = numberChars.length;
while (patternIndex > 0) {
patternIndex--;
numberIndex--;
char numberChar = numberChars[numberIndex];
char patternChar = patternChars[patternIndex];
switch (patternChar) {
case 'A':
if (A == null) {
A = numberChar;
B = (char) (A + 1);
C = (char) (B + 1);
D = (char) (C + 1);
} else if (!A.equals(numberChar)) {
return false;
}
break;
case 'B':
if (B == null) {
B = numberChar;
A = (char) (B - 1);
C = (char) (B + 1);
D = (char) (C + 1);
} else if (!B.equals(numberChar)) {
return false;
}
break;
case 'C':
if (C == null) {
C = numberChar;
B = (char) (C - 1);
A = (char) (B - 1);
D = (char) (C + 1);
} else if (!C.equals(numberChar)) {
return false;
}
break;
case 'D':
if (D == null) {
D = numberChar;
C = (char) (D - 1);
B = (char) (C - 1);
A = (char) (B - 1);
} else if (!D.equals(numberChar)) {
return false;
}
break;
case 'X':
if (X == null) {
X = numberChar;
} else if (!X.equals(numberChar)) {
return false;
}
break;
case 'Y':
if (Y == null) {
Y = numberChar;
} else if (!Y.equals(numberChar)) {
return false;
}
break;
case 'Z':
if (Z == null) {
Z = numberChar;
} else if (!Z.equals(numberChar)) {
return false;
}
break;
case '.':
break;
case '0':
if (numberChar != '0') {
return false;
}
break;
}
}
return true;
}
}

Infix to postfix digit or letter concatenation

working on a program that converts infix notation to postfix. I have it working for most instances except when character concatenation is required. For example, if I pass in a string of numbers (1002+304) it outputs 1, 0, 0, 2, 3, 0, 4, + instead of 1002, 304, +.
import java.util.*;
public class InfixToPostfix {
private Deque<String> postfix; // Used as a queue of String
private static boolean isOperator(char op)
{
if(op=='+'||op=='-'||op=='*'||op=='/'||op=='^'
||op=='('||op==')')
{
return true;
}
return false;
}
private static boolean lowerEqualPrec(char op1, char op2)
{
boolean flag = false;
if(op1=='+'|| op1=='-')
{
if(op2=='+'||op2=='-'||op2=='*'||op2=='/'||op2=='^')
{
flag= true;
}
}else if(op1=='*' || op1=='/')
{
if(op2=='*'||op2=='/'||op2=='^')
{
flag= true;
}
}else if(op1=='^')
{
flag= false;
}else if(op1=='(')
{
flag= false;
}
return flag;
}
public InfixToPostfix(String infix)
{
for(int i=0; i<infix.length(); i++)
{
if(infix.length() ==0 || infix.charAt(0)==')' ||
infix.charAt(i)=='&' || infix.charAt(infix.length()-1)=='(')
{
throw new IllegalArgumentException();
}
}
postfix = new LinkedList<String>();
Stack<Character> stack = new Stack<Character>();
Character ch;
String digits="";
String letters = "";
for(int i=0; i<infix.length(); i++)
{
ch=infix.charAt(i);
if(ch == ' ')
{
//do nothing
}
if(Character.isDigit(ch))
{
digits=""+ch;
if(i+1 >= infix.length() || !Character.isDigit(infix.charAt(i+1)))
{
digits=digits+"";
}
postfix.add(digits);
}
if(Character.isLetter(ch))
{
letters=ch+"";
postfix.add(letters);
}
if(isOperator(ch))
{
if(ch == ')')
{
if(!stack.isEmpty() && stack.peek() != '(')
{
postfix.add(""+stack.pop());
if(!stack.isEmpty())
{
stack.pop();
}
}
}
else
{
if(!stack.isEmpty() && !lowerEqualPrec(ch, stack.peek()))
{
stack.push(ch);
}
else
{
while(!stack.isEmpty() && lowerEqualPrec(ch, stack.peek()))
{
char pop = stack.pop();
if(ch!='(')
{
postfix.add(pop+"");
}
}
stack.push(ch);
}
}
}
}
while(!stack.isEmpty()&&stack.peek()!='(')
{
postfix.add(stack.pop()+"");
}
System.out.println(postfix);
}
public Iterator<String> iterator()
{
return new PostfixIterator(postfix) ;
}
public static void main(String[] args)
{
InfixToPostfix test = new InfixToPostfix("1002+304");
}
}
For postfixConversion
public static String postfixConversion(String input) {
int i;
String postfix = "";
Stack<Character> stack = new Stack<Character>();
for (i = 0; i < input.length(); i++) {
while (input.charAt(i) == ' ') {
++i;
}
if (Character.isDigit(input.charAt(i))) {
postfix += input.charAt(i);
//if (!Character.isDigit(input.charAt(i+1))) {
postfix += ' ';
//}
}
else if (precedenceLevel(input.charAt(i)) != 0) {
while ((!stack.isEmpty()) && (precedenceLevel(stack.peek()) >= precedenceLevel(input.charAt(i))) && (stack.peek() != '(')) {
postfix += stack.peek();
postfix += ' ';
stack.pop();
}
stack.push(input.charAt(i));
}
else if (input.charAt(i) == '(') {
stack.push(input.charAt(i));
}
else if (input.charAt(i) == ')') {
while (!stack.isEmpty() && stack.peek() != '(') {
postfix += stack.peek();
stack.pop();
}
stack.pop();
}
}
while (!stack.isEmpty()) {
postfix += stack.peek();
postfix += ' ';
}
return postfix;
}

Checking brackets nesting in a string

I took a training challenge on Codility that checks for the proper nesting of brackets in a string. The brackets to be checked are {,},(,),[,]. I have written the following java program which passes in O(n) time and space, but I have a feeling that the extra space I use can be reduced. Also I think that there must be a data structure that can handle this scenario more efficiently. Using an ArrayList instead of an array might help. What I need here is a critique of my code. Thanks in advance.
Here is the code I wrote:
import java.util.HashMap;
class Solution {
public int solution(String S) {
char[] stack = new char[S.length()];
int last = -1;
HashMap hm = new HashMap();
hm.put('}', '{');
hm.put(')', '(');
hm.put(']', '[');
for(int i=0; i<S.length(); i++){
char next = S.charAt(i);
if(next == '}' || next == '{' || next == ')' || next == '(' ||
next == ']' || next == '[')
{
if(last!=-1 && hm.containsKey(next) && stack[last] == hm.get(next)){
last--;
}
else{
last++;
stack[last] = S.charAt(i);
}
}
}
if(last == -1){
return 1;
}
return 0;
}
}
Here is a solution with a list:
import java.util.LinkedList;
class Solution {
public int solution(String S) {
LinkedList<Character> stack = new LinkedList<>();
for (int i = 0; i < S.length(); i++) {
char c = S.charAt(i);
if (c == '{' || c == '[' || c == '(') {
stack.push(c);
} else {
if (stack.isEmpty()) {
return 0;
}
char preceding = stack.pop();
if (c == ')' && preceding != '(') {
return 0;
}
if (c == ']' && preceding != '[') {
return 0;
}
if (c == '}' && preceding != '{') {
return 0;
}
}
}
return stack.isEmpty() ? 1 : 0;
}
}
javascript sulution for codility Brackets
function solution(S) {
var head = 0,
stack = [],
s_ln = S.length;
d = {
"(" : ")",
"{" : "}",
"[" : "]"
};
for(var i = 0; i < s_ln; i++) {
if(d[S[i]]) {
stack[head++] = S[i];
} else {
if(d[stack[head-1]] === S[i]) {
head--;
} else {
return 0;
}
}
if (head < 0) return 0;
}
return head === 0 ? 1 : 0;
}
result 100% 100%
Here is my javascript solution which also scores 100/100 in correctness and performance on codility:
https://codility.com/demo/results/trainingXB4S2W-D3F/
function solution(text) {
var openBrackets = ['(', '[', '<', '{'];
var closedBrackets = [')',']','>','}'];
var requiredClosedBrackets = [];
var chars = text.split('');
var result = true;
for (var i = 0; i < chars.length; i++) {
for (var j = 0; j < openBrackets.length; j++) {
if (chars[i] == openBrackets[j]) {
requiredClosedBrackets.push(closedBrackets[j]);
} else if (chars[i] == closedBrackets[j]) {
if (chars[i] != requiredClosedBrackets[requiredClosedBrackets.length - 1]) {
return 0;
} else {
requiredClosedBrackets.pop();
}
}
}
}
return (requiredClosedBrackets.length == 0) ? 1 : 0;
}
Java 100/100
https://codility.com/demo/results/demo97TPVG-CPP/
I'm including a Stack definition that's why is a little bit longer but the solution is simple:
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
class Solution {
public static final int BALANCED = 1;
public static final int UNBALANCED = 0;
public int solution(String S) {
if (S.isEmpty()) return BALANCED;
Stack<Character> stack = new Stack<>(S.length());
NestedValidatorUtil util = new NestedValidatorUtil();
for (char c: S.toCharArray()) {
if (stack.isEmpty()){
if (util.isOpener(c)) {
stack.push(c);
} else {
return UNBALANCED;
}
} else {
if(util.isOpener(c)) {
stack.push(c);
} else if(util.getOpenerForGivenCloser(c) == stack.peek()){
stack.pop();
} else {
return UNBALANCED;
}
}
}
return stack.isEmpty() ? BALANCED : UNBALANCED;
}
public static class NestedValidatorUtil {
private Map<Character, Character> language = new HashMap<Character, Character>(){{
put(')','(');
put(']','[');
put('}','{');
}};
public boolean isOpener(Character c) {
return language.values().contains(c);
}
public Character getOpenerForGivenCloser(Character closer) {
return language.get(closer);
}
}
public static class Stack<T> {
public List<T> stack;
public Stack(int capacity) {
stack = new ArrayList<>(capacity);
}
public void push(T item) {
stack.add(item);
}
public T pop() {
T item = peek();
stack.remove(stack.size() - 1);
return item;
}
public T peek() {
int position = stack.size();
T item = stack.get(position - 1);
return item;
}
public boolean isEmpty() {
return stack.isEmpty();
}
}
}
Not Java, but JavaScript, it still can give some inspiration
function checkBrackets(str) {
var brackets = { '(': ')', '{': '}', '[': ']' };
var container = { ')': 1, '}': 1, ']': 1 };
var passed = true;
for (var i = 0, len = str.length; i < len; i++) {
if (brackets[str[i]]) {
var match = brackets[str[i]];
container[match] = container[match] ? container[match]+1 : 1;
} else if (container[str[i]]) {
container[str[i]]--;
}
}
for (var br in container) {
if (container[br] !== 1) { return false; }
}
return passed;
}
console.log(checkBrackets('(])'));
console.log(checkBrackets('()'));
console.log(checkBrackets('(()'));
console.log(checkBrackets('({[]})'));
I will attempt to answer this question in 2016. :-p.
The code below passes 100/100 and simple to understand.
package codility.com;
import java.util.Stack;
/**
* Created by rattanak on 6/29/2016.
*/
public class Solution_Stack_Queue {
public static void main(String[] args) {
String s = "()()";
System.out.print(perfectNesting(s));
}
//Q: https://codility.com/c/run/trainingB4J4DF-UZU
// S empty : return 1;
// (()(())()) return 1
// (() : return 0
// Use of stack to save chars
public static int perfectNesting(String s) {
if (s.length() == 0) return 1;
if (s.length() % 2 == 1) return 0; //odd
Stack<Character> st = new Stack<Character>();
for (int i =0; i < s.length(); i++){
char ch = (char)s.charAt(i);
if (ch == '(' || ch == '{' || ch == '[' ){
st.push(s.charAt(i));
} else { //found )
if (st.isEmpty()) return 0;
if (st.peek() != '('){
return 0;
} else {
//remove
st.pop();
}
}
}
if (st.isEmpty()) return 1;
return 0;
}
}
Result: https://codility.com/demo/results/training8XUE3W-JTX/
Let me know in comments below if you have any questions.

Categories

Resources