Codingbat.com endX not resolving "other tests" - java

I'm working on a recursion problem from codingbat.com which states
Given a string, compute recursively a new string where all the lowercase 'x' chars have been moved to the end of the string.
It passes all of the examples except for the example that says "other tests." Since i can't see what "other tests" are referring to, I'm stuck. Any help would be appreciated. Here's my code
public String endX(String str) {
return endX2(str, 0, str.length());
}
public String endX2(String str, int n, int len){
if(len == 0) return str;
if(n == len-1) return str;
if(str.substring(n,n+1).equals("x")){
return str.substring(0,n) + (endX2(str.substring(n+1) + "x", n, len-1));
}
else return endX2(str, n+1, len);
}

I'm not sure why you have an additional method as it's unnecessary. You also have an additional check - if(n == len-1) return str; - that isn't needed.
The problem you're facing is the fact you're using unchecked indices and are getting lucky when the strings used do not end with an 'x' or a number of 'x's. If I use your code against String xs = "xfooxbarxx"; gets me a java.lang.StringIndexOutOfBoundsException. I haven't debugged the code extensively, but that should bring some understanding to the reason why it fails on the "other" tests. Take a look at my version and investigate for yourself where the issue may lie and how to perhaps make your code a bit more concise.
public String endX(String str) {
if(str.length() == 0)
return "";
return str.startsWith("x") ?
endX(str.substring(1)) + "x" :
String.valueOf(str.charAt(0)) + endX(str.substring(1));
}

PS: This is WAY WAY longer than it needs to be.
/*
Alright here is what we need to do...
Step 1: Get all the 'x' chars into a String.
Step 2: Get all NON 'x' chars into a String.
Step 3 (goal): Concencate (combine) the NON 'x' String first then the 'x' String in that order.
Solution Notes: Instead of using an index variable to go through a String, we could 'substring' off the first char in the String each time, cutting until we are down to the base case, for the sake of showing recursive logic I used an index varible. However on Arrays or any other collection, you need an index varible to access that element ant that spot or (index).
*/
public String endX(String str) {
//Ternary operator used here...could be written as a full if then else statement.
//Ternary operator logic: if the length is 0 return "", else return getNonX(str, "", 0) + getX(str, "", 0);
return (str.length() == 0) ? "" : getNonX(str, "", 0) + getX(str, "", 0);
//NOTICE in the parts [getNonX(str, "", 0)] and [getX(str, "", 0)]
//there is an empty String in the middle, that is there to hold or gather the
//chars, 'x' or not. We fill those empty Strings up in each recursive helper
}
public String getX(String str, String x, int index) {
//We are at the end, and if the last char IS an 'x'...
if(index == str.length() - 1 && str.charAt(index) == 'x'){
return x + 'x'; //...put that last 'x' on the 'x' String.
}
//We are at the end and if the last char IS NOT an 'x'...
else if(index == str.length() - 1 && str.charAt(index) != 'x'){
return x; //...just return what we got.
}
//When we see an 'x' but we aren't at the end...
if(index < str.length() - 1 && str.charAt(index) == 'x'){
x += 'x'; //...append 'x' to the 'x' String.
}
return getX(str, x, index + 1); //recur, moving the index up
}
public String getNonX(String str, String nonX, int index) {
//We are at the end, and if the last char IS NOT an 'x'...
if(index == str.length() - 1 && str.charAt(index) != 'x'){
return (nonX + str.charAt(index)); //...append that char to the 'nonX' String
}
//We are at the end and if the last char IS an 'x'...
else if(index == str.length() - 1 && str.charAt(index) == 'x'){
return nonX; //...just return what we got.
}
//When we see a non 'x' char and we aren't at the end...
if(index < str.length() - 1 && str.charAt(index) != 'x'){
nonX += str.charAt(index); //...append that char to the 'nonX' String
}
return getNonX(str, nonX, index + 1); //recur, move the index up
}

You can try this code...
public String endX(String str) {
if(str.length() <=1) return str;
if(str.charAt(0) == 'x') return endX(str.substring(1)) + 'x';
return str.charAt(0) + endX(str.substring(1));
}

Related

How to let my code also check strings with length 0?

I am trying to remove the letter x if it is present as the first or last character of any given string. Why does my code not check strings with length 0 even though I have accounted for it in my code?
I have already tried using an if statement to check if the length is 0 and, if so, to return the original string. I have also tried returning an empty string with the same value.
public String withoutX(String str) {
if (((str.charAt(0)=='x') || str.charAt(str.length()-1) == 'x') && str.length()>=2){
if (str.charAt(0)=='x'){
str = str.substring(1,str.length());
} if (str.charAt(str.length()-1) == 'x'){
str = str.substring(0,str.length()-1);
}
} if (str.length()==1 && str == "x"){
return "";
} if (str.length()==0){
return str;
// the above if statement (length = 0) does not work
} else{
return str;
}
}
The expected result is for it to return the string without the letter x. This has been achieved for all strings, except an empty one, where it says that the index is out of range.
if (str.length() == 0) {
return str;
}
It should be at the top of the method body. Or, at least, it should go prior to getting str.charAt(0).
There's String#isEmpty.
The way you compared Strings is wrong. Use equals instead of ==.
Always validate the length before accessing a letter by index. Make sure that index in a string exists.
In this line:
if (((str.charAt(0)=='x') || str.charAt(str.length()-1) == 'x') && str.length()>=2)
you do check for the length of the string but after you check the indexes.
Change to:
if (str.length()>=2 && (str.charAt(0)=='x' || str.charAt(str.length()-1) == 'x'))
if str.length()>=2 is false the other conditions will not be checked because of Short Circuit Evaluation.
Also use equals() to compare strings and not ==.
I vwould write your method like this:
public String withoutX(String str) {
if (str.length() == 0 || (str.length() == 1 && str.equals("x")))
return "";
if (str.charAt(0) == 'x') {
str = str.substring(1, str.length());
}
if (str.charAt(str.length() - 1) == 'x') {
str = str.substring(0, str.length() - 1);
}
return str;
}
I kept your logic and removed unnecessary code.
I think this is a better implementation of what you are trying to do
public String withoutX(String str) {
if (str.startsWith("x")){
str = str.replaceFirst("x", "");
}
if (str.endsWith("x")){
str = str.substring(0,str.lastIndexOf("x"));
}
return str;
}
A simpler approach without making many corner-cases checking is to use regex to remove the leading and trail x - if it exists. Here's the code:
public String withoutX(String str){
return "".equals(str)? str: str.replaceAll("(^x|x$)", "");
}
Your code is checking for a x before you check if the string is empty. You should move
if (str.length()==0){
return str;
// the above if statement (length = 0) does not work
}
to the first line of the method. Besides that your code is great.
public String withoutX(String str) {
if (!str.isEmpty() && str.charAt(0)=='x'){
str = str.substring(1, str.length());
}
if (!str.isEmpty() && str.charAt(str.length()-1) == 'x'){
str = str.substring(0,str.length()-1);
}
return str;
}
You can first check, if the str is empty and remove the first x. Then check the size for greater than 1 and remove the last x. This leads to less and more readable code.

How can I count the number of some specific characters in a string recursively?

I was practicing recursion problem. there are some type of questions wants you to count the number of specific characters in a String without loop.
What kind of method should I use? can anyone explain to me?
Here is the question:"Given a string, compute recursively (no loops) the number of lowercase 'x' chars in the string."
my code:
public int countX(String str) {
if(str.length()==0){
return 0;
}
if(str.charAt(0)=='x'){
return 1+ countX(str.substring(1));
}
return countX(str);
}
You're almost there, but you need to revise the case where the first letter in str isn't 'x'. What you are currently doing will cause infinite recursion because in the case where str.charAt(0) != 'x', you recursively call countX on str. However, str is still the same string that didn't have 'x' as its first letter, so it's going to call countX on str again and again and again, etc.. So the solution is to only call countX on str.substring(1) when its first letter isn't 'x' like so,
public int countX(String str) {
if(str.length()==0){
return 0;
}
if(str.charAt(0)=='x'){
return 1 + countX(str.substring(1));
}
else{
return countX(str.substring(1));
}
}
What your method was doing before
Let's say I called countX on "Hello", here's how the call stack trace would look like,
call countX("Hello")
"Hello".length() != 0 so we move on to the next condition
"Hello".charAt(0) != 'x' so we call countX("Hello")
call countX("Hello")
"Hello".length() != 0 so we move on to the next condition
"Hello".charAt(0) != 'x' so we call countX("Hello")
call countX("Hello")
"Hello".length() != 0 so we move on to the next condition
"Hello".charAt(0) != 'x' so we call countX("Hello");
.
.
.
infinite recursion
What the new solution does
call countX("Hello")
"Hello".length() != 0 so we move on to the next condition
"Hello".charAt(0) != 'x' so we call countX("ello")
call countX("ello")
"ello".length() != 0 so we move on to the next condition
"ello".charAt(0) != 'x' so we call countX("llo")
call countX("llo")
"llo".length() != 0 so we move on to the next condition
"llo".charAt(0) != 'x' so we call countX("lo");
call countX("lo")
"lo".length() != 0 so we move on to the next condition
"lo".charAt(0) != 'x' so we call countX("o");
call countX("o")
"o".length() != 0 so we move on to the next condition
"o".charAt(0) != 'x' so we call countX("");
call countX("")
"".length() == 0 so return 0
return 0
return 0
return 0
return 0
return 0
Notice how in the solution, we always have a way of getting to the base case (str.length()==0). Whereas before, there would be instances (when we encountered a letter that wasn't x) that would prevent the method from reaching the base case.
What kind of method should I use? can anyone explain to me?
one solution is to use a StringBuilder and simply delete the first character at each invocation of the method, eventually, you'll get to the base case and should output the correct result.
public int countX(String str) {
if(str.length() == 0) return 0;
StringBuilder builder = new StringBuilder(str);
if(str.charAt(0) == 'x'){
builder.deleteCharAt(0);
return 1 + counter(builder.toString());
}
builder.deleteCharAt(0);
return counter(builder.toString());
}
However, if you wish to proceed with your current solution, you'll need to replace this:
return countX(str);
with this:
return countX(str.substring(1));
the problem was that if the current character is not 'x' you simply ignored it and didn't simplify the problem towards the base case.
I understand your need. In below, you have the method you need to count recursively a character in a string.
static int countCharRecursively(String str, char ch){
if(str.indexOf(ch) != -1)
{
return countCharRecursively(str.substring(0, str.indexOf(ch)), ch)+
countCharRecursively(str.substring(str.indexOf(ch)+1),ch) + 1;
}
else{
return 0;
}
}
Here is a complete program you can run.
public class CountCharRecursivelyInString{
public static void main(String[] args){
String str = "hello the world";
char ch = 'l';
int nbr = countCharRecursively(str, ch);
System.out.println(ch + " : " + nbr);
}
static int countCharRecursively(String str, char ch){
if(str.indexOf(ch) != -1)
{
return countCharRecursively(str.substring(0, str.indexOf(ch)), ch)+
countCharRecursively(str.substring(str.indexOf(ch)+1),ch) + 1;
}
else{
return 0;
}
}
}

Rewriting a for loop as recursive method?

I am having trouble rewriting the following code as a recursive method rather than using the for loop. The for loop tests to see if the String 'noSpaces' is a palindrome (the same forwards as it is backwards). The noSpaces String has no punctuation, spaces, or differences in capitalization.
Thanks for the help
public boolean isRegularPalindrome(String noSpaces) {
noSpaces = noSpaces.toUpperCase();
String[] letters = new String[noSpaces.length()];
for (int i = 0; i < letters.length; i++) {
letters[i] = Character.toString(noSpaces.charAt(i));
}
for (int i = 0; i < letters.length / 2; i++) {
if (!letters[i].equals(letters[letters.length - i - 1])) {
return false;
}
}
return true;
}
There you go:
public static boolean isPalindrome(String input) {
if (input.charAt(0) != input.charAt(input.length() - 1)) {
// Missmatch. Not a palindrome!
return false;
} else if (input.length() > 1){
// If there is more to test, continue.
return isPalindrome(input.substring(1, input.length() - 1));
} else {
// All chars were tested, or 1 char input. Palindrome!
return true;
}
}
Writing a recursive algorithm for anything requires base cases. For a palindrome, this would be a string of length 0 or length 1 -- if the string is length 0 or 1, it is a palindrome.
If the base cases aren't met, you check the first character against the last character.
If the characters aren't the same, return false.
If the characters are the same, return the recursive call to the string except for the first and last characters.
The code should look something like this.
public boolean isPalindrome(string str){
if (str.length == 0)
return true;
else if (str.length == 1)
return true;
else if(str.charAt(0) != str.charAt(str.length - 1)
return false;
else
return isPalindrome(str.substring(1, length - 1));
}

Java if-statement being skipped

The method below takes in a string and a pattern and returns true if they match each other. A '.' matches 1 char and a '*' matches 0 or more (e.g. expMatch("abc", "a.c") should return true). I added a bunch of print statements to see where I went wrong and it seems like the if statement is being skipped even if the str.length() == 1.
I call it with System.out.println(expMatch("abc", "a*c"));
Here is the code:
public static boolean expMatch(String str, String pat)
{
if (str.charAt(0) == pat.charAt(0) || pat.charAt(0) == '.')
{
System.out.println("in if");
System.out.println(str.charAt(0));
System.out.println(pat.charAt(0));
System.out.println(str.length());
if (str.length() == 1)
return true;
expMatch(str.substring(1), pat.substring(1));
}
else if (pat.charAt(0) == '*')
{
System.out.println("in else");
System.out.println(str.charAt(0));
System.out.println(pat.charAt(0));
if (str.length() == 1)
return true;
if (str.charAt(0) == pat.charAt(1)) //val of * = 0
expMatch(str, pat.substring(1));
else if (str.charAt(1) ==pat.charAt(1))
expMatch(str.substring(1), pat.substring(1));
}
return false;
}
and the output is:
in if
a
a
3
in else
b
*
in if
c
c
1
false
Even if the length is 1 it skips the if? Any idea why?
P.S. I'm not looking for the solution, just why the if statement is being skipped.
You always return false from the method at the very end. You are calling expmatch recursively but never using the return value. The code comes in to the first if, recurses (because length is not 1) and upon returning will go to the final return statement which returns false.
You need to add a return before your expMatch() calls - because the false comes from your last line return false;
What happens is this:
you call expMatch() with the two Strings.
you enter the if clause
the if clause enters expMatch() recursively
you enter the else clause
the else clause enters expMatch() recursively
you enter the if clause again
you leave the expMatch() method
you leave the other expMatch method
false is returned
Your approach is logically incorrect even if you apply the fixes the others suggested. Try this test case:
System.out.println(expMatch("abddddc", "a*c"));
This is because when you encounter a * in the pattern, you have no way to know how many characters "to eat" from the search string.
To say the least, you need a loop somewhere, not just an if. Let me try to fix it for you (not sure if it's possible though, not sure if you always know which path to take, I mean in your recursion). Think some more about it. Here is another unpleasant test case:
System.out.println(expMatch("adddcac", "a*c"));
// the * needs to eat dddca (despite the c present in dddca),
// it should not stop recursing there at that c
I think you need some sort of full search here.
Just an if or a while loop is not good enough.
EDIT: Here is a fixed version with a bunch of nasty tests. I think this is called non-linear recursion (as it's not a single path you try). Not 100% sure though about that term.
public class Test055 {
public static void main(String[] args) {
// System.out.println(expMatch("abddddc", "a*c"));
System.out.println(expMatch("adcax", "a*c"));
System.out.println(expMatch("adcax", "a*c*"));
System.out.println(expMatch("adcacm", "*"));
System.out.println(expMatch("adcacmmm", "a*c"));
System.out.println(expMatch("adcacmmmc", "a*c"));
System.out.println(expMatch("adcac", "a*c"));
System.out.println(expMatch("adcacxb", "a*c.b"));
System.out.println(expMatch("adcacyyb", "a*c.b"));
System.out.println(expMatch("adcacyyb", "a*c*b"));
}
public static boolean expMatch(String str, String pat)
{
// System.out.println("=====================");
// System.out.println("str=" + str);
// System.out.println("pat=" + pat);
if (pat.length() == 0 && str.length() > 0) {
return false;
} else if (pat.length() == 0 && str.length() == 0) {
return true;
} else if (pat.charAt(0) == '.'){
return str.length() >= 1 && expMatch(str.substring(1), pat.substring(1));
}else if (pat.charAt(0) != '*'){
return str.length() >= 1 && pat.charAt(0) == str.charAt(0) && expMatch(str.substring(1), pat.substring(1));
}else{
// Now let's handle the tricky part
// (1) Look for the 1st non-star in pattern
int k=-1;
char ch = ' ';
for (int i=0; i<pat.length(); i++){
if (pat.charAt(i) != '*'){
k = i;
ch = pat.charAt(k);
break;
}
}
if (k==-1){
// (2A) only stars found in pattern, OK, any str matches that
return true;
}else{
// (2B) do full search now checking all
// possible candidate chars in str that
// match the char ch from pattern
for (int i=0; i<str.length(); i++){
if (str.charAt(i)==ch){
boolean b = expMatch(str.substring(i+1), pat.substring(k+1));
if (b) return true;
}
}
return false;
}
}
}
}

Recursive Substrings out of bounds error

I have to create a recursive method to display all substrings of a given string before the letter 'A' or 'a', and ignore that letter in the process. The termination condition works fine. However, in the continue condition I am thrown a indexoutofbounds error and I'm not entirely sure why. As far as I can tell I stop the loop before the index reaches the string's length. but I will post it here in case I missed something.
class Tree
{
void subStrings(String s)
{
if(s.length() == 1)
{
if(s.charAt(0) == 'A' || s.charAt(0) == 'a')
{
System.out.println("Cannot shorten substring.");
}
else
{
System.out.println(s);
}
}
else
{
String subString = "";
int i = 0;
while(s.charAt(i) != 'A' && i < s.length())//bad line
{
subString += s.charAt(i);
i++;
}
if(subString.equals(""))
subStrings(s.substring(i));
else
{
System.out.println(subString);
subStrings(s.substring(i));
}
}
}
int treeHeight(String tree)
{
return 0;
}
}
Even Robby's refactoring won't get you where you won't on account of some other issues. For what concerns your exception you must iterate to i < s.length() - 1 since you're incrementing the index in the loop, and charAt method you use inside the loop starts at index 0.
Checked further and you should change your substring(i) to subStrings(s.substring(0, i)) otherwise you would end up with the same string in recursion. The following should work for you
void subStrings(String s)
{
if(s == null || s.length() == 0 || s.charAt(0) == 'A' || s.charAt(0) == 'a')
{
System.out.println("Cannot shorten substring.");
return;
}
if(s.length() != 1)
{
String subString = "";
int i = 0;
while(s.charAt(i) != 'A' && s.charAt(i) != 'a' && i < s.length() - 1)//bad line
{
subString += s.charAt(i);
i++;
}
if(subString.equals(""))
subStrings(s.substring(i));
else
{
System.out.println(subString);
subStrings(s.substring(0, i));
}
}
}
You need to reverse these two conditions:
while(s.charAt(i) != 'A' && i < s.length()) { /*...*/ }
So, it should be:
while(i < s.length() && s.charAt(i) != 'A') { /*...*/ }
Otherwise you get an out of bounds exception when the string is empty and you try to access the first character (at position 0).
If you just want to split a string using a or A as a delimiter, you might as well do:
String[] substrings = str.split("[aA]");
If it absolutely has to be implemented using a recursive method, instead of processing the string character by character, you could use indexOf to find the position of the next a or A. It could look something like this:
public static void subStrings(String s) {
int i = s.toLowerCase().indexOf('a');
if (i >= 0) {
System.out.println(s.substring(0, i));
if (i + 1 < s.length()) {
subStrings(s.substring(i + 1));
}
}
}

Categories

Resources