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));
}
}
}
Related
I'm having a bit of trouble trying to create a recursive method that counts the number of periods and spaces in a string. I can do this pretty easily using iteration, but I'm still fairly new to the concept of recursion. Here's my code so far,
could anyone tell me where I'm going wrong?
public static int periodsAndSpaces(String s){ //option 3
if(s.length()<0){ //base case
return 0;
}
else if(s.charAt(0) == ' ' || s.charAt(0) == '.'){ //general case
return periodsAndSpaces(s.substring(1)) + 1;
}
return 0;
}
package com.test.demo;
public class Counter {
public static void main(String[] args) {
System.out.println(new Counter().countPeriodsAndSpaces(" test. . .a"));
}
int countPeriodsAndSpaces(String rs) {
if (rs == null || rs.isEmpty())
return 0;
char c = rs.charAt(0);
if (c == ' ' || c == '.')
return 1 + countPeriodsAndSpaces(rs.substring(1));
else
return countPeriodsAndSpaces(rs.substring(1));
}
}
// Output 6
New method with everyone's suggestions:
public static int periodsAndSpaces(String s){ //option 3
if(s.length()==0){ //base case
return 0;
}
else if(s.charAt(0) == ' ' || s.charAt(0) == '.'){
return periodsAndSpaces(s.substring(1)) + 1;
}
else{
return periodsAndSpaces(s.substring(1));
}
}
Method stops running when there are no more characters left in the string, and I've added another case for adding onto the sum of periods and spaces when the current char doesn't have either of these.
The rest of the code is working perfectly but I cannot figure out how to prevent punctuation from being translated.
public class PigLatintranslator
{
public static String translateWord (String word)
{
String lowerCaseWord = word.toLowerCase ();
int pos = -1;
char ch;
for (int i = 0 ; i < lowerCaseWord.length () ; i++)
{
ch = lowerCaseWord.charAt (i);
if (isVowel (ch))
{
pos = i;
break;
}
}
if (pos == 0 && lowerCaseWord.length () != 1) //translates if word starts with vowel
{
return lowerCaseWord + "way"; // Adding "way" to the end of string
}
else if (lowerCaseWord.length () == 1) //Ignores words that are only 1 character
{
return lowerCaseWord;
}
else if (lowerCaseWord.charAt(0) == 'q' && lowerCaseWord.charAt(1) == 'u')//words that start with qu
{
String a = lowerCaseWord.substring (2);
return a + "qu" + "ay";
}
else
{
String a = lowerCaseWord.substring (1);
String b = lowerCaseWord.substring (0,1);
return a + b + "ay"; // Adding "ay" at the end of the extracted words after joining them.
}
}
public static boolean isVowel (char ch) checks for vowel
{
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' || ch == 'y')
{
return true;
}
return false;
}
}
I need the translation to ignore punctuation. For example "Question?" should be translated to "estionquay?" (question mark still in the same position and not translated)
As Andreas said, if the function is expecting only one word, it should be the responsibility of the calling function to ensure there's no full sentence or punctuation being passed to it. With that said, if you require the translator to handle this, you need to find the index of the string where the punctuation or non-letter character occurs. I added in a main method to test the function:
public static void main(String[] args) {
System.out.println(translateWord("QUESTION?"));
}
I added a loop into the qu case to find the punctuation being input, the two checks are to see if the character at position i is inside the range of a - z. The sub-string then only goes to the point where the punctuation is found.
int i;
for (i = 0; i < lowerCaseWord.length(); i++) {
if(lowerCaseWord.charAt(i) > 'z' || lowerCaseWord.charAt(i) < 'a') {
break;
}
}
String a = lowerCaseWord.substring (2, i);
String b = lowerCaseWord.substring(i);
return a + "qu" + "ay" + b;
This may need some tweaking if you're worried about words with hyphens and whatnot but this should put across the basic idea.
Here's the output I received:
$javac PigLatintranslator.java
$java -Xmx128M -Xms16M PigLatintranslator
estionquay?
All i need this method to do is take a string lets say "aaxbb" and it will return true because there is an 'aa' and a 'bb',
If the string given is length = 0 or length = 1 it should fail.
The issue i'm having is that which i do not know.. i know that in my terminal after hasAdjacentPair's first test case Pass's, i get a blinking curser meaning that somehwere in this method i'm not kicking out of one of my loops for it to continue to check the string for any more adjacent pairs
the first test case passes while its an empty string "" = because it returned false
the second test case passes while its "a" = because it returned false
We are also not allowed to use Arrays :(
public boolean hasAdjacentPair(String str)
{
boolean result = false;
if (str.length() == 0)
{
result = false;
}
if (str.length() == 1)
{
result = false;
}
while (str.length() != 0)
{
for (int i = 0; i < str.length() - 1; ++i)
{
char adjChar = str.charAt(i);
char nextAdjChar = str.charAt(i + 1);
if (adjChar == nextAdjChar)
{
result = true;
}
}
}
return result;
}
Changed my while loop while (str.length() != 0) to while (str.length() != 0 && str.length() != 1) this enabled test 2 to work
EDIT 2 : After i completely took out the while (str.length() != 0) All 5 of my test cases pass :) so i guess it was just that?
while (str.length() != 0)
is always true and loop never end. Instead of if..if..while structure use either switch on length of string or if-else.
You could try something like this
if (str.length() == 0)
{
result = false;
}
else if (str.length() == 1)
{
result = false;
}
else
{
for (int i = 0; i < str.length() - 1; ++i)
{
char adjChar = str.charAt(i);
char nextAdjChar = str.charAt(i + 1);
if (adjChar == nextAdjChar)
{
result = true;
break;
}
}
If you checking for atleast one pair make use of break statement as you already know that you got the result
try this alternative:
boolean c = false;
//Your other code
while (str.length() != 0 && c == false)
{
for (int i = 0; i < str.length() - 1; ++i)
{
char adjChar = str.charAt(i);
char nextAdjChar = str.charAt(i + 1);
if (adjChar == nextAdjChar)
{
result = true;
}
}
c = true;
}
//Your other code
Is there a reason you have a while loop checking for the str length? The str is not modified in the loop.
What you most likely want is an if, else if, else structure.
You can most likely return true within the adjChar == nextAdjChar if statement and return false at the end of your function.
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));
}
Here is the problem: Return true if the string "cat" and "dog" appear the same number of times in the given string. Examples: catDog("catdog") → true; catDog("catcat") → false; catDog("1cat1cadodog") → true
public boolean catDog(String str) {
int countCat=0;
int countDog=0;
for (int i=0; i<str.length();i++)
{
if (str.charAt(i)== 'c'&& str.length()>=3)
{
if (str.substring(i,i+3).equals("cat"))
countCat++;
}
}
for (int i=0; i<str.length();i++)
{
if (str.charAt(i)== 'd' && str.length()>=3)
{
if (str.substring(i,i+3).equals("dog"))
countDog++;
}
}
if (countCat == countDog)
return true;
else
return false;
}
In your for loops conditions you are checking if entire String has length greater or equal 3 instead of checking only part from i till end. Try maybe with
str.length() - i >= 3
instead of
str.length() >= 3
str.substring(i,i+3).equals("cat")
i might be the last and i+3 will give an error
Why don't you simply use StringUtils#countMatches?
StringUtils.countMatches(myStr, "cat") == StringUtils.countMatches(myStr, "dog");
Don't get lost with the indexes. However, if you don't want to use this method, debugging your code is the best thing you can do.
Okay, this is what I might do:
The problem was with your check str.length() >= 3. It should have been i + str.length().
I also suggest some changes to your code to get rid of duplication. Here I extracted the part that counts the number of appearances of a substring and moved it to its own method. The part that checks if count of cat equals count of dog now calls said method twice.
public static void main(String[] args) {
System.out.println(catDog("catdog"));
System.out.println(catDog("catcat"));
System.out.println(catDog("1cat1cadodog"));
System.out.println(catDog("catdogcatc"));//Would previously throw error.
}
public static boolean catDog(String str) {
int countCat = countAppearances(str, "cat");
int countDog = countAppearances(str, "dog");
return countCat == countDog;
}
private static int countAppearances(String str, String key) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == key.charAt(0) && i + key.length() <= str.length()) {
if (str.substring(i, i + key.length()).equals(key)) {
count++;
}
}
}
return count;
}
You need to update your first condition before spiting you string like:
if (str.charAt(i)== 'c' && (str.length() - i) >= 3)
{
if (str.substring(i,i+3).equals("cat"))
countCat++;
}
public boolean catDog(String str) {
int catCount = 0, dogCount = 0;
//run a for loop to check cat count
//run loop till 2nd last character
for (int i = 0; i < str.length() - 2; i++) {
//now check if the charaters at positions matches "cat"
//if matches then increment cat count
if (str.charAt(i) == 'c' && str.charAt(i + 1) == 'a' && str.charAt(i + 2) == 't') {
catCount++;
} else if (str.charAt(i) == 'd' && str.charAt(i + 1) == 'o' && str.charAt(i + 2) == 'g') {
//else check if the charaters at positions matches "dog"
//if matches then increment dog count
dogCount++;
}
}
//check cat count and dog count
if (catCount == dogCount) {
return true;
} else {
return false;
}
}