I am trying to understand recursion and solve the problem with
operators = ['','*', "+"]
input : "2224"
target : 24
output = {"22+2", "2+22", "24"}
Here's the code that I came up with. But it produces invalid output.
static List<String> output = new ArrayList<>();
static String[] generate_all_expressions(String s, long target) {
getExpressionsRecur(s, target, 0, null, 0);
String[] out = new String[output.size()];
return output.toArray(out);
}
static void getExpressionsRecur(String s, long target, int currentValue, String currExpression, int currIndex) {
if (currIndex == s.length()){
if (currentValue == target) {
output.add(currExpression);
}
return;
}
if (currentValue == target) {
output.add(currExpression);
return;
}
int currentPart = Integer.valueOf(s.substring(currIndex, currIndex+1));
if (currIndex == 0) {
getExpressionsRecur(s, target, currentPart, String.valueOf(currentPart), currIndex+1);
} else {
int value = Integer.valueOf(String.valueOf(currentValue) + String.valueOf(currentPart));
getExpressionsRecur(s, target, value , currExpression + "" + currentPart, currIndex+1);
getExpressionsRecur(s, target, (currentValue * currentPart), currExpression + "*" + currentPart, currIndex+1);
getExpressionsRecur(s, target, (currentValue + currentPart), currExpression + "+" + currentPart, currIndex+1);
}
}
It produces:
{22+2, 2*2+2*4, 2+2+2*4}
Can someone help me spot the errors?
First rethink your recursion since you're not hitting every case.
Write out cases if you need to, maybe start with a 3 digit number since there are less cases; In your code, for example, "24" is never evaluated on it's own.
You are calculating value wrongly for concatenation case. 22*2 will become 22*24.But you are saying this new expression has a value of 444 (should be 528) . You can't really use current value. Probably you can restructure your recursion to make it easier.
You can use this Expression object I threw together in replace of your expression String... call expression.toString() and expression.value() as necessary. But still you'll need changes to your basic recursion structure even after implementing this or similar Expression Object.
public class Expression{
Expression lExpression;
String lValue;
Expression rExpression;
String operator;
public Expression(String expression) {
int multIndex = expression.indexOf("*");
int addIndex = expression.indexOf("+");
if(multIndex == -1 && addIndex == -1) {
this.lExpression = new Expression(Integer.valueOf(expression));
return;
}
if(multIndex != -1 && multIndex < addIndex) {
this.lExpression = new Expression(expression.substring(0, addIndex));
this.operator = expression.substring(addIndex, addIndex+1);
this.rExpression = new Expression(expression.substring(addIndex+1));
}else {
if(addIndex == -1) {
addIndex = Integer.MAX_VALUE;
}
if(multIndex == -1) {
multIndex = Integer.MAX_VALUE;
}
int opIndex = multIndex < addIndex ? multIndex : addIndex;
this.lExpression = new Expression(expression.substring(0, opIndex));
this.operator = expression.substring(opIndex, opIndex+1);
this.rExpression = new Expression(expression.substring(opIndex+1)); }
}
public Expression(int value) {
this.lValue = String.valueOf(value);
this.lExpression = null;
this.rExpression = null;
this.operator = null;
}
#Override
public String toString() {
return (lExpression!=null ? lExpression : lValue) + (operator !=null ? operator: "") + (rExpression!=null ? rExpression : "");
}
public int value() {
if(lExpression == null) {
return Integer.valueOf(lValue);
}
if("*".equals(operator)) {
return lExpression.value() * rExpression.value() ;
}
if("+".equals(operator)) {
return lExpression.value() + rExpression.value();
}
return lExpression.value();
}
}
Related
Given an arbitrary number of TextField inputs (t1, t2, t3, ...) and a custom boolean string input from a JtextArea, I need to check if lines in a file match the custom boolean expression. It needs to support nested parentheses.
Example:
User enters "str1" into t1 , "str2" into t2, "str3" into t3, "str4" into t4, "str5" into t5.
User enters the following into the JTextArea for the custom boolean:
"not ((t1 and not t3) or (t4 and t2)) or t5"
Then based on these inputs, I must filter a file and return lines in the file that match the custom boolean based on a "contains" relationship (e.g. "t1 and not t3" means a line must contain the string t1 and not contain the string t3).
For example a file with the following two lines:
str 5
str4 str2
The filter would only return str5 because it is the only line that matches the custom boolean.
I am having trouble even getting started. I have tried to think of a recursive solution but couldn't come up with anything. Also I tried non-recursive solutions but can't come up with anything either.
There is also the problem of the end result boolean needing to take in a parameter (each line in the file). I thought of maybe producing a sequence of operations to perform rather than a boolean that somehow takes in a parameter. But I can't figure out how to get this sequence in the first place.
Here is what I have now. It is very bad and I am thinking of scrapping this approach.
public class CustomInputParser {
private ArrayList<String> pairs;
private String inp;
private HashMap<Integer,String> atomMap;
public CustomInputParser() {
this.pairs = null;
this.inp = "";
this.atomMap = new HashMap<Integer,String>();
}
public void findAtoms() {
int i = 0;
for(String s : this.pairs) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
if(!inp.substring(begin+1, end).contains("(")) {
this.pairs.set(i, this.pairs.get(i) + ",#");
}
i++;
}
}
public void computeAtoms() {
int i = 0;
for(String s : this.pairs) {
if(s.contains(",#")) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
//this.pairs.set(i,this.pairs.get(i).replace(",a", ""));
this.pairs.set(i, this.pairs.get(i) + ","+inp.substring(begin+1, end));
this.atomMap.put(begin,this.pairs.get(i).split(",")[3]+"#"+String.valueOf(end));
}
i++;
}
System.out.println(this.pairs.toString());
System.out.println(this.atomMap.toString());
}
public void replaceAtoms() {
int i = 0;
for(String s : this.pairs) {
if(!(s.contains("o") || s.contains("a") || s.contains("n"))) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0])+1;
int end = Integer.valueOf(indices[1]);
for(int j = begin; j < end; j++) {
if(inp.charAt(j) == '(') {
if(atomMap.containsKey(j)) {
this.pairs.set(i, this.pairs.get(i) + ","+j+"#"+atomMap.get(j).split("#")[1]+">"+atomMap.get(j).split("#")[0]);
}
else {
this.pairs.set(i,"!"+ this.pairs.get(i));
}
}
}
}
i++;
}
System.out.println(this.pairs.toString());
}
public ArrayList<String> getPairs(String str){
this.inp = str;
ArrayList<String> res = new ArrayList<String>();
char[] arr = str.toCharArray();
Stack<Integer> s = new Stack<Integer>();
for(int i = 0; i < arr.length; i++) {
if(arr[i] == '(') {
s.push(i);
}
if(arr[i] == ')') {
if(s.empty()) {
return null;
}
else {
Integer start = s.pop();
Integer end = Integer.valueOf(i);
res.add(start.toString() + "," + end.toString());
}
}
}
if(!s.empty()) {
return null;
}
this.pairs = res;
return res;
}
public static void main(String[] args) {
String x = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
System.out.println(x);
CustomInputParser c = new CustomInputParser();
System.out.println(c.getPairs(x).toString());
c.findAtoms();
c.computeAtoms();
c.replaceAtoms();
}
}
The first step is to tokenize the input. Define
enum Token {VAR, LP, RP, NOT, AND, OR, END}
LP and RP are parentheses. Now define a tokenizer class that looks something like this:
class Tokenizer {
Tokenizer(String input) {...}
void reset() {...}
Token getNext() {...}
String getVarName() {...}
}
Calling getNext() on your example in a loop should return
LP LP NOT VAR AND LP LP NOT VAR OR VAR RP OR LP VAR OR VAR RP RP RP OR LP VAR AND NOT VAR RP RP AND VAR END
Calling getVarName() immediately after a VAR has been returned by getNext() gives you the name of the variable (e.g. "t42").
There are many ways to implement little scanners like this. You should do this first and make sure it's bulletproof by testing. Trying to build a parser on top of a flaky scanner is torture.
As I said in comments, I'd consider recursive descent parsing. If you have a suitable grammar, writing an RD parser is a very short step as the Dragon Book (also mentioned above) shows.
A reasonable grammar (using tokens as above) is
Expr -> Term AND Term
| Term OR Term
| Term END
Term -> NOT Term
| Opnd
Opnd -> VAR
| LP Expr RP
For example, here is how you'd get started. It shows the first rule converted to a function:
class Evaluator {
final Tokenizer tokenizer = ...; // Contains the expression text.
final Map<String, Boolean> env = ... // Environment: variables to values.
Token lookAhead; // Holds the token we're parsing right now.
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) { ... }
void advance() {
lookAhead = tokenizer.getNext();
}
boolean expr() {
boolean leftHandSide = term(); // Parse the left hand side recursively.
Token op = lookAhead; // Remember the operator.
if (op == Token.END) return leftHandSide; // Oops. That's all.
advance(); // Skip past the operator.
boolean rightHandSide = term(); // Parse the right hand side recursively.
if (op == Token.AND) return leftHandSide && rightHandSide; // Evaluate!
if (op == Token.OR) return leftHandSide || rightHandSide;
dieWithSyntaxError("Expected op, found " + op);
}
boolean term() {...}
boolean opnd() {...}
}
The environment is used when a VAR is parsed. Its boolean value is env.get(tokenizer.getVarName()).
So to process the file, you'll
For each line
For each variable tX in the expression
See if the line contains the string tX is bound to in its text field.
If so, put the mapping tX -> true in the environment
else put tX -> false
Reset the tokenizer
Call Evaluator.evaluate(tokenizer, environment)
If it returns true, print the line, else skip it.
This is the simplest approach I can think of. About 150 lines. Many optimizations are possible.
Added
Well since I can no longer take away the thrill of discovery, here is my version:
import static java.lang.Character.isDigit;
import static java.lang.Character.isWhitespace;
import java.util.HashMap;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
public class TextExpressionSearch {
enum Token { VAR, LP, RP, NOT, AND, OR, END }
static class Tokenizer {
final String input;
int pos = 0;
String var;
Tokenizer(String input) {
this.input = input;
}
void reset() {
pos = 0;
var = null;
}
String getRead() {
return input.substring(0, pos);
}
Token getNext() {
var = null;
while (pos < input.length() && isWhitespace(input.charAt(pos))) {
++pos;
}
if (pos >= input.length()) {
return Token.END;
}
int start = pos++;
switch (input.charAt(start)) {
case 't':
while (pos < input.length() && isDigit(input.charAt(pos))) {
++pos;
}
var = input.substring(start, pos);
return Token.VAR;
case '(':
return Token.LP;
case ')':
return Token.RP;
case 'n':
if (input.startsWith("ot", pos)) {
pos += 2;
return Token.NOT;
}
break;
case 'a':
if (input.startsWith("nd", pos)) {
pos += 2;
return Token.AND;
}
break;
case 'o':
if (input.startsWith("r", pos)) {
pos += 1;
return Token.OR;
}
break;
}
throw new AssertionError("Can't tokenize: " + input.substring(start));
}
}
static class Evaluator {
final Tokenizer tokenizer;
final Map<String, Boolean> env;
Token lookAhead;
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) {
this.tokenizer = tokenizer;
this.env = env;
advance();
}
boolean die(String msg) {
throw new AssertionError(msg + "\nRead: " + tokenizer.getRead());
}
void advance() {
lookAhead = tokenizer.getNext();
}
void match(Token token) {
if (lookAhead != token) {
die("Expected " + token + ", found " + lookAhead);
}
advance();
}
boolean evaluate() {
boolean exprVal = expr();
match(Token.END);
return exprVal;
}
boolean expr() {
boolean lhs = negated();
switch (lookAhead) {
case AND:
advance();
return negated() && lhs;
case OR:
advance();
return negated() || lhs;
case END:
return lhs;
}
return die("Expected expr, found " + lookAhead);
}
boolean negated() {
switch (lookAhead) {
case NOT:
advance();
return !negated();
default:
return operand();
}
}
boolean operand() {
switch (lookAhead) {
case VAR:
if (!env.containsKey(tokenizer.var)) {
die("Undefined variable: " + tokenizer.var);
}
boolean varVal = env.get(tokenizer.var);
advance();
return varVal;
case LP:
advance();
boolean exprVal = expr();
match(Token.RP);
return exprVal;
}
return die("Expected operand, found " + lookAhead);
}
}
public static void main(String [] args) {
String expr = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
Map<String, String> bindings = new HashMap<>();
bindings.put("t1", "str1");
bindings.put("t2", "str2");
bindings.put("t3", "str3");
bindings.put("t4", "str4");
bindings.put("t5", "str5");
bindings.put("t6", "str6");
bindings.put("t7", "str7");
String [] lines = {"str5 str7", "str4 str2"};
Tokenizer tokenizer = new Tokenizer(expr);
for (String line : lines) {
Map<String, Boolean> env =
bindings.entrySet().stream()
.collect(toMap(e -> e.getKey(), e -> line.contains(e.getValue())));
tokenizer.reset();
if (new Evaluator(tokenizer, env).evaluate()) {
System.out.println(line);
}
}
}
}
You can define a parser that returns a Predicate<String> that tests if a given string satisfies a conditional expression.
static Predicate<String> parse(String s, Map<String, String> map) {
return new Object() {
String[] tokens = Pattern.compile("[()]|[a-z][a-z0-9]*")
.matcher(s).results()
.map(MatchResult::group)
.toArray(String[]::new);
int length = tokens.length;
int index = 0;
String token = get();
String get() {
return token = index < length ? tokens[index++] : null;
}
boolean eat(String expect) {
if (expect.equals(token)) {
get();
return true;
}
return false;
}
Predicate<String> identifier() {
String id = token;
return s -> {
String value = map.get(id);
if (value == null)
throw new RuntimeException(
"identifier '" + id + "' undefined");
return s.contains(value);
};
}
Predicate<String> factor() {
boolean not = false;
Predicate<String> p;
if (eat("not"))
not = true;
switch (token) {
case "(":
get();
p = expression();
if (!eat(")"))
throw new RuntimeException("')' expected");
break;
case ")": case "not": case "and": case "or":
throw new RuntimeException("syntax error at '" + token + "'");
default:
p = identifier();
get();
break;
}
if (not)
p = p.negate();
return p;
}
Predicate<String> term() {
Predicate<String> p = factor();
while (eat("and"))
p = p.and(factor());
return p;
}
Predicate<String> expression() {
Predicate<String> p = term();
while (eat("or"))
p = p.or(term());
return p;
}
Predicate<String> parse() {
Predicate<String> p = expression();
if (token != null)
throw new RuntimeException("extra tokens string");
return p;
}
}.parse();
}
test case:
#Test
public void testParse() {
String s = "not ((t1 and not t3) or (t4 and t2)) or t5";
Map<String, String> map = new HashMap<>(Map.of(
"t1", "str1",
"t2", "str2",
"t3", "str3",
"t4", "str4",
"t5", "str5"));
Predicate<String> p = parse(s, map);
assertTrue(p.test("str5"));
assertTrue(p.test("str3"));
assertTrue(p.test("str1 str3"));
assertFalse(p.test("str1"));
assertFalse(p.test("str2 str4"));
// you can change value of variables.
assertFalse(p.test("str1 FOO"));
map.put("t5", "FOO");
assertTrue(p.test("str1 FOO"));
}
syntax:
expression = term { "or" term }
term = factor { "and" factor }
factor = [ "not" ] ( "(" expression ")" | identifier )
identifier = letter { letter | digit }
letter = "a" | "b" | ... | "z"
digit = "0" | "1" | ... | "9"
For posterity, here is my shunting yard solution which includes input validation:
public class CustomInputParser {
private Stack<Character> ops;
private LinkedList<Character> postFix;
private HashMap<Character, Integer> precedence;
private Stack<Boolean> eval;
private HashMap<Integer, String> termsMap;
private String customBool;
public CustomInputParser(HashMap<Integer, String> tMap, String custBool) {
this.ops = new Stack<Character>();
this.eval = new Stack<Boolean>();
this.postFix = new LinkedList<Character>();
this.termsMap = tMap;
this.customBool = custBool;
this.precedence = new HashMap<Character, Integer>();
precedence.put('n', 1);
precedence.put('a', 2);
precedence.put('o',3);
precedence.put('(', 4);
}
private int inToPost() {
char[] expr = convertToArr(this.customBool);
char c;
for(int i = 0; i < expr.length; i++) {
c = expr[i];
if(isOp(c)) {
if(processOp(c) != 0) return -1;
}
else {
if(!Character.isDigit(c)) {
return -1;
}
//I made the mistake of using a queue of Characters for postfix initially
//This only worked for up to 9 operands (multi digit would add mutiple chars to
// postfix for a single reference.
//This loops is a lazy workaround:
// 1. get the string of the reference (e.g. "11")
// 2. convert it to int
// 3. store the char value of the int in postfix
// 4. when evaluating operands in postfix eval, convert char back to int to get the termsMap key
String num = "";
while(i < expr.length) {
if(!Character.isDigit(expr[i])) {
i--;
break;
}
c = expr[i];
num += c;
i++;
}
int j = Integer.valueOf(num);
c = (char) j;
postFix.offer(c); //enqueue
}
}
while(!ops.empty()) {
if(ops.peek() == '(')return -1; //no matching close paren for the open paren
postFix.offer(ops.pop()); //pop and enqueue all remaining ops from stack
}
return 0;
}
private boolean isOp(char c) {
if(c == '(' || c == ')' || c =='n' || c=='a' || c=='o') {
return true;
}
return false;
}
private int processOp(char c) {
if (ops.empty() || c == '(') {
ops.push(c);
}
else if(c == ')') {
while(ops.peek() != '(') {
postFix.offer(ops.pop()); //pop and equeue ops wrapped in parens
if(ops.empty()) return -1; //no matching open paren for the close paren
}
ops.pop(); // don't enqueue open paren, just remove it from stack
}
else if(precedence.get(c) > precedence.get(ops.peek())) {
postFix.offer(ops.pop()); //pop and enqueue the higher precedence op
ops.push(c);
}
else {
ops.push(c);
}
return 0;
}
public boolean evaluate(String s) {
while(!postFix.isEmpty()) {
char c = postFix.poll();
boolean op1, op2;
switch(c) {
case 'n':
op1 = eval.pop();
eval.push(!op1);
break;
case 'a':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 && op2);
break;
case 'o':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 || op2);
break;
default:
int termKey = (int) c;
String term = this.termsMap.get(termKey);
eval.push(s.contains(String.valueOf(term)));
break;
}
}
return eval.pop();
}
private char[] convertToArr(String x) {
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
return x.toCharArray();
}
public static void main(String[] args) {
String customBool = "(t1 and not (t2 and t3)) or (t4 and not t5)";
HashMap<Integer,String> termsMap = new HashMap<Integer, String>();
termsMap.put(1,"str1");
termsMap.put(2,"str2");
termsMap.put(3,"str3");
termsMap.put(4,"str4");
termsMap.put(5,"str5");
CustomInputParser c = new CustomInputParser(termsMap, customBool);
if(c.inToPost() != 0) {
System.out.println("invalid custom boolean");
}
else {
System.out.println(c.evaluate("str1str5"));
}
}
}
This is the question:
Write a recursive method that removes all consecutively occurring letters from a string of fixed size. E.g. “AAAbbCCCC” becomes “AbC”
My Code:
public static String NoRepeats(String n, int start) {
String x = "";
if(start == n.length()-1) {
return x;
}
if(n.charAt(start) == n.charAt(start+1)) {
return NoRepeats(n, start+1);
}
else {
x += n.charAt(start);
return NoRepeats(n,start+=1);
}
}
ok, so I wasn't sure why it would only return an empty string, So I fiddled around with the syntax.
FYI String n = "AAAABBBBCCCCDDDD"
In my recursive steps I couldn't use 'start ++ or start +1", it only worked when it was 'start +=1'. This will correct it.
This is my new code:
public static String NoRepeats(String n, int start) {
String x = "";
if(start == (n.length()-1)) {
x += n.charAt(start);
return x;
}
if(n.charAt(start) == n.charAt(start+1)) {
return NoRepeats(n, start+=1);
}
else {
x += n.charAt(start);
return x +NoRepeats(n,start+=1);
}
}
I was wondering why did the above returned empty string so I went and modify your codes to see how it's done.
String a = "";
String b = "";
try {
a = Tesst1.NoRepeats("AAAbbCCCC", 0, b);
} catch(Exception e) {
e.printStackTrace();
}
System.out.println(a);
}
public static String NoRepeats(String n, int start, String b) {
if(start == n.length()-1) {
return b += n.charAt(start - 1);
}
if(n.charAt(start) == n.charAt(start+1)) {
return NoRepeats(n, start+1, b);
}
else {
b += n.charAt(start);
return NoRepeats(n,start+1, b);
}
}
This should now yield AbC.
In your code x is local to function. So, with every call you lose the value of x. Also, there was little mistake in first statement.
In classic way, you could use the result of function:
public static String noRepeats(String n, int start) {
return (start == n.length() - 1) ? "" + n.charAt(start) :
(n.charAt(start) == n.charAt(start + 1)) ? noRepeats(n, start + 1) :
n.charAt(start) + noRepeats(n, start + 1);
public static void main(String[] args) {
System.out.println(noRepeats("AAAbbCCCC", 0));
}
Or need some buffer to accumulate new characters:
public static String noRepeats(String n) {
return noRepeats(n, new StringBuilder(), 0);
}
public static String noRepeats(String n, StringBuilder result, int start) {
if(start == n.length() - 1) {
return result.append(n.charAt(start)).toString();
}
if(n.charAt(start) == n.charAt(start + 1)) {
return noRepeats(n, result, start + 1);
}
else {
result.append(n.charAt(start));
return noRepeats(n, result, start + 1);
}
}
public static void main(String[] args) {
System.out.println(noRepeats("AAAbbCCCC"));
}
I want to write a function that return true if if and only if
String a is always followed by String b, and String b is always followed by string c, this is what I wrote but it doesn't work :
public static boolean follows2(String s, String a, String b, String c) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b))) {
res = false;
if (!(charAtPos(s, i + 2).equals(c))) {
res = false;
}
}
}
}
return res;
}
public static void main(String[] args) {
System.out.println(follows2(" koali oliali ", "a", "l", "i"));
// RETURN TRUE OK since for each " a" is followed by "l" then "i"
System.out.println(follows2("ipoipa", "i", "p", "a"));
//RETURN TRUE BUT IT'S NOT !
// The first " i" is followed by " p" then "o" which is not good
}
Here is the function that I wrote for:
String a is always followed by String b ( It works )
public static boolean follows(String s, String a, String b) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b))) {
res = false;
}
}
}
return res;
}
public static String charAtPos(String s, int i) {
return String.valueOf(s.charAt(i));
}
public static void main(String[] args) {
System.out.println(follows("Arnold Barney", "r", "n"));
// RETURN TRUE because each "r" are followed by the letter "n"
System.out.println(follows("Arnold Barney", "n", "o"));
// RETURN FALSE , because not every "n" is followed by "o"
}
What can be done in my first program to make it work ?
Thank you
With recursion:
public static boolean follows(String s, String a, String b, String c) {
int ai = s.indexOf(a);
if (ai == -1) {
return true; // No more 'a' string, we're all good
}
int bi = s.indexOf(a + b);
int ci = s.indexOf(a + b + c);
if (bi != ai || ci != ai) {
return false; // Strings 'b' and 'bc' don't follow 'a', so the check failed
}
return follows(s.substring(ai + a.length()), a, b, c);
}
In reality, bi could be removed.
The problem you have is that you are not actually entering the nested if statements due to a minor flaw in your logic.
I would change it to this which checks whether i + 1 is equal to String b || i + 2 is equal to String c
public static boolean follows2(String s, String a, String b, String c) {
boolean res = true;
for (int i = 0; i < s.length(); i++) {
if (charAtPos(s, i).equals(a)) {
if (!(charAtPos(s, i + 1).equals(b)) || !(charAtPos(s, i + 2).equals(c))) {
res = false;
}
}
}
return res;
}
Because the code doesn't do what you think it does.
if you'll add some prints to the code, you'll see you never set "res" to false.
Let's debug the case you're testing:
When meet the the first letter - "i" - it entered the first if.
the next letter is "p" so you're not entered the 2nd if, so you'll continue to the next letter in the "for".
Here's my attempt (haven't tested it)
boolean hasOnlyCompleteSequences(String source, char.. chars) {
for (int s = 0; s < source.length(); s++) {
int c = 0;
if (source.charAt(s) == chars[c]) {
if (!isCompleteSequence(source, s + 1, chars, c + 1)) {
return false;
}
}
}
return true;
}
boolean isCompleteSequence(String source, int s, char[] chars, int c) {
while (s < source.length() && c < chars.length) {
// note: the indices get increased AFTER comparison
if (source.charAt(s++) != chars[c++]) {
return false;
}
}
// cover the case that the source String ends with an incomplete sequence
if (s == source.length() && c < chars.length) {
return false;
}
return true;
}
This is a much cleaner answer (tested & working great):
public static boolean follows(String s, String...strings) {
int number = 0;
for(int i = 0; i<s.length(); i++){
if(strings[number].length()+i<s.length() && s.substring(i, strings[number].length()+i).equals(strings[number]) && number+1 != strings.length) number++;
else if(number!=0 && !s.substring(i, strings[number].length()+i).equals(strings[number])) return false;
else number = 0;
}
return true;
}
It fixes many problems with your code:
Working with strings but using "charAt"
Copying the function just to add a parameter
I have been working on this problem for several hours now and I just cannot figure out what I am doing wrong here. Could anyone help point me in the right direction?
I was asked to write an Autocomplete program and I've completed everything except for this one method I cannot get working. Each term has: 1. String query and 2. long weight.
Here is the method:
public static Comparator<Term> byReverseWeightOrder() {
return new Comparator<Term>() { // LINE CAUSING PROBLEM
public int compare(Term t1, Term t2) {
if (t1.weight > t2.weight) { // LINE CAUSING PROBLEM
return -1;
} else if (t1.weight == t2.weight) {
return 0;
} else {
return 1;
}
}
};
}
My problem is that no matter how I mess with the method I always result in a NullPointerException(). Which, it points to this method (byReverseWeightOrder) as well as these two statements.
Arrays.sort(matches, Term.byReverseWeightOrder());
Term[] results = autocomplete.allMatches(prefix);
Here is the rest of the code if it can be found helpful:
Term
import java.util.Comparator;
public class Term implements Comparable<Term> {
public String query;
public long weight;
public Term(String query, long weight) {
if (query == null) {
throw new java.lang.NullPointerException("Query cannot be null");
}
if (weight < 0) {
throw new java.lang.IllegalArgumentException("Weight cannot be negative");
}
this.query = query;
this.weight = weight;
}
public static Comparator<Term> byReverseWeightOrder() {
return new Comparator<Term>() {
public int compare(Term t1, Term t2) {
if (t1.weight > t2.weight) {
return -1;
} else if (t1.weight == t2.weight) {
return 0;
} else {
return 1;
}
}
};
}
public static Comparator<Term> byPrefixOrder(int r) {
if (r < 0) {
throw new java.lang.IllegalArgumentException("Cannot order with negative number of characters");
}
final int ref = r;
return
new Comparator<Term>() {
public int compare(Term t1, Term t2) {
String q1 = t1.query;
String q2 = t2.query;
int min;
if (q1.length() < q2.length()) {
min = q1.length();
}
else {
min = q2.length();
}
if (min >= ref) {
return q1.substring(0, ref).compareTo(q2.substring(0, ref));
}
else if (q1.substring(0, min).compareTo(q2.substring(0, min)) == 0) {
if (q1.length() == min) {
return -1;
}
else {
return 1;
}
}
else {
return q1.substring(0, min).compareTo(q2.substring(0, min));
}
}
};
}
public int compareTo(Term that) {
String q1 = this.query;
String q2 = that.query;
return q1.compareTo(q2);
}
public long getWeight() {
return this.weight;
}
public String toString() {
return this.weight + "\t" + this.query;
}
}
BinarySearchDeluxe
import java.lang.*;
import java.util.*;
import java.util.Comparator;
public class BinarySearchDeluxe {
public static <Key> int firstIndexOf(Key[] a, Key key, Comparator<Key> comparator) {
if (a == null || key == null || comparator == null) {
throw new java.lang.NullPointerException();
}
if (a.length == 0) {
return -1;
}
int left = 0;
int right = a.length - 1;
while (left + 1 < right) {
int middle = left + (right - left)/2;
if (comparator.compare(key, a[middle]) <= 0) {
right = middle;
} else {
left = middle;
}
}
if (comparator.compare(key, a[left]) == 0) {
return left;
}
if (comparator.compare(key, a[right]) == 0) {
return right;
}
return -1;
}
public static <Key> int lastIndexOf(Key[] a, Key key, Comparator<Key> comparator) {
if (a == null || key == null || comparator == null) {
throw new java.lang.NullPointerException();
}
if (a == null || a.length == 0) {
return -1;
}
int left = 0;
int right = a.length - 1;
while (left + 1 < right) {
int middle = left + (right - left)/2;
if (comparator.compare(key, a[middle]) < 0) {
right = middle;
} else {
left = middle;
}
}
if (comparator.compare(key, a[right]) == 0) {
return right;
}
if (comparator.compare(key, a[left]) == 0) {
return left;
}
return -1;
}
}
AutoComplete
import java.util.Arrays;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
public class Autocomplete {
public Term[] terms;
public Autocomplete(Term[] terms) {
if (terms == null) {
throw new java.lang.NullPointerException();
}
this.terms = terms.clone();
Arrays.sort(this.terms);
}
public Term[] allMatches(String prefix) {
if (prefix == null) {
throw new java.lang.NullPointerException();
}
Term theTerm = new Term(prefix, 0);
int start = BinarySearchDeluxe.firstIndexOf(terms, theTerm, Term.byPrefixOrder(prefix.length()));
int end = BinarySearchDeluxe.lastIndexOf(terms, theTerm, Term.byPrefixOrder(prefix.length()));
int count = start;
System.out.println("Start: " + start + " End: " + end);
if (start == -1 || end == -1) {
// System.out.println("PREFIX: " + prefix);
throw new java.lang.NullPointerException();
} // Needed?
Term[] matches = new Term[end - start + 1];
//matches = Arrays.copyOfRange(terms, start, end);
for (int i = 0; i < end - start; i++) {
matches[i] = this.terms[count];
count++;
}
Arrays.sort(matches, Term.byReverseWeightOrder());
System.out.println("Finished allmatches");
return matches;
}
public int numberOfMatches(String prefix) {
if (prefix == null) {
throw new java.lang.NullPointerException();
}
Term theTerm = new Term(prefix, 0);
int start = BinarySearchDeluxe.firstIndexOf(terms, theTerm, Term.byPrefixOrder(prefix.length()));
int end = BinarySearchDeluxe.lastIndexOf(terms, theTerm, Term.byPrefixOrder(prefix.length()));
System.out.println("Finished numberMatches");
return end - start + 1; // +1 needed?
}
public static void main(String[] args) throws IOException {
// Read the terms from the file
Scanner in = new Scanner(new File("wiktionary.txt"));
int N = in.nextInt(); // Number of terms in file
Term[] terms = new Term[N];
for (int i = 0; i < N; i++) {
long weight = in.nextLong(); // read the next weight
String query = in.nextLine(); // read the next query
terms[i] = new Term(query.replaceFirst("\t",""), weight); // construct the term
}
Scanner ip = new Scanner(System.in);
// TO DO: Data Validation Here
int k;
do {
System.out.println("Enter how many matching terms do you want to see:");
k = ip.nextInt();
} while (k < 1 || k > N);
Autocomplete autocomplete = new Autocomplete(terms);
// TO DO: Keep asking the user to enter the prefix and show results till user quits
boolean cont = true;
do {
// Read in queries from standard input and print out the top k matching terms
System.out.println("Enter the term you are searching for. Enter * to exit");
String prefix = ip.next();
if (prefix.equals("*")) {
cont = false;
break;
}
Term[] results = autocomplete.allMatches(prefix);
System.out.println(results.length);
for(int i = 0; i < Math.min(k,results.length); i++)
System.out.println(results[i].toString());
} while(cont);
System.out.println("Done!");
}
}
I apologize for the sloppy code, I have been pulling my hair out for awhile now and keep forgetting to clean it up.
Two examples:
Example 1:
int k = 2;
String prefix = "auto";
Enter how many matching terms do you want to see:
2
Enter the term you are searching for. Enter * to exit
auto
619695 automobile
424997 automatic
Example 2:
int k = 5;
String prefix = "the";
Enter how many matching terms do you want to see:
5
Enter the term you are searching for. Enter * to exit
the
5627187200 the
334039800 they
282026500 their
250991700 them
196120000 there
This code:
import org.custommonkey.xmlunit.Diff;
String result = "<ns1:Square xsi:type=\"ns1:Shape\" xmlns:ns1=\"http://example.com/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>";
String correct = "<ns2:Square xsi:type=\"ns2:Shape\" xmlns:ns2=\"http://example.com/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>";
Diff diff = new Diff(result, correct);
System.out.println("diff:" + diff);
System.out.println("diff.similar(): " + diff.similar());
results in:
diff: org.custommonkey.xmlunit.Diff
[not identical] Expected namespace prefix 'ns1' but was 'ns2' - comparing <ns1:Square...> at /Square[1] to <ns2:Square...> at /Square[1]
[different] Expected attribute value 'ns1:Shape' but was 'ns2:Shape' - comparing <ns1:Square xsi:type="ns1:Shape"...> at /Square[1]/#type to <ns2:Square xsi:type="ns2:Shape"...> at /Square[1]/#type
diff.similar(): false
I would expect diff.similar() to be true.
or is there a reason why it is false? or it is a bug?
it returns true if I remove the xsi:type info.
any idea how to fix it?
XMLUnit doesn't understand xsi type . It does a simple string comparison for attribute value.
Implements a custom DifferenceListener do the trick
final Diff d = new Diff(result, correct);
d.overrideDifferenceListener(new DifferenceListener() {
public int differenceFound(Difference difference) {
final Node controlNode = difference.getControlNodeDetail().getNode();
final Node testNode = difference.getTestNodeDetail().getNode();
if (difference.getId() == DifferenceConstants.ATTR_VALUE_ID
&& isXSIType(controlNode) && isXSIType(testNode)) {
if (getNameSpaceFromPrefix(controlNode).compareTo(getNameSpaceFromPrefix(testNode)) != 0) {
return RETURN_ACCEPT_DIFFERENCE;
}
String withoutPrefixControl = getNameWithoutPrefix(controlNode);
String withoutPrefixTest = getNameWithoutPrefix(testNode);
if (withoutPrefixControl.compareTo(withoutPrefixTest) == 0) {
return RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
}
return RETURN_ACCEPT_DIFFERENCE;
}
boolean isXSIType(org.w3c.dom.Node node) {
return node.getNodeType() == Node.ATTRIBUTE_NODE &&
node.getLocalName().compareTo("type") == 0 &&
node.getNamespaceURI() == "http://www.w3.org/2001/XMLSchema-instance";
}
private String getNameSpaceFromPrefix(Node node) {
final int beginIndex = node.getNodeValue().indexOf(":");
if (beginIndex == -1) {
return "";
}
return node.lookupNamespaceURI(node.getNodeValue().substring(0, beginIndex));
}
private String getNameWithoutPrefix(Node controlNode) {
final int beginIndex = controlNode.getNodeValue().indexOf(":");
if (beginIndex == -1) {
return controlNode.getNodeValue();
}
return controlNode.getNodeValue().substring(beginIndex);
}
public void skippedComparison(org.w3c.dom.Node node, org.w3c.dom.Node node1) {
}
});
For XMLUnit 2.x add this to diff builder
.withDifferenceEvaluator(new DifferenceEvaluator() {
public ComparisonResult evaluate(Comparison comparison, ComparisonResult comparisonResult) {
// skip namespace prefix comparison
if (comparison.getType().equals(ComparisonType.NAMESPACE_PREFIX))
return ComparisonResult.SIMILAR;
return comparisonResult;
}
})