My assignment is to write methods for a class where I test for palindromes that a user enters. The class must have a recursive method and that method must call a helper method that removes spaces, punctuation, and ignores case.
I have two working classes that do these things but I'm wondering which structure works better and which helper method actually fits the description of a helper method.
Here's the first class:
public class RecursivePalindrome
{
public boolean Palindrome(String s)
{
return PalindromeHelper(s);
}
public boolean PalindromeHelper(String s)
{
String a = s.toLowerCase(); //Converts any capital letters to lowercase beforte analyzing the string
a = a.replaceAll(" ", ""); //Removes any and all spaces in the string
for(int i = 0; i < a.length(); i++) //Removes punctuation by using isLetter method from Character Class
{
if(Character.isLetter(a.charAt(i)) == false)
a = a.replace(a.substring(i, i+1), "");
}
if(a.length() == 0 || a.length() == 1)
return true;
else if(a.charAt(0) == (a.charAt(a.length() - 1)))
return PalindromeHelper(a.substring(1, a.length() - 1));
else
return false;
}
}
And the second one:
public class Recurs
{
public boolean Palindrome(String s)
{
String l = PalindromeHelper(s);
if(l.length() == 0 || l.length() == 1)
return true;
else if(l.charAt(0) == (l.charAt(l.length() - 1)))
return Palindrome(l.substring(1, l.length() - 1));
else
return false;
}
public String PalindromeHelper(String s)
{
s = s.toLowerCase(); //Converts any capital letters to lowercase before analyzing the string
s = s.replaceAll(" ", ""); //Removes any and all spaces in the string
for(int i = 0; i < s.length(); i++) //Removes punctuation by using isLetter method from Character Class
{
if(Character.isLetter(s.charAt(i)) == false)
s = s.replace(s.substring(i, i+1), "");
}
return s;
}
}
I would write it this way.
class RecursivePalindrome
{
public boolean Palindrome(String s)
{
//Think about using a stringbuilder instead of a string.
String a = s.toLowerCase(); // Converts any capital letters to lowercase
// beforte analyzing the string
a = a.replaceAll(" ", ""); // Removes any and all spaces in the string
for (int i = 0; i < a.length(); i++) // Removes punctuation by using
// isLetter method from Character
// Class
{
if (Character.isLetter(a.charAt(i)) == false)
a = a.replace(a.substring(i, i + 1), "");
}
return validatePalindrome(a);
}
public boolean validatePalindrome(String s)
{
if (s.length() == 0 || s.length() == 1)
return true;
else if (s.charAt(0) == (s.charAt(s.length() - 1)))
return PalindromeHelper(s.substring(1, s.length() - 1));
else
return false;
}
}
A couple of things before we get to the code...
A "helper" method is more usually called a utility method, which is a stateless piece of code - being stateless:
it should be declared as static
Adhering to java naming conventions is a great idea, so:
method names start with a lowercase letter
boolean methods start with is if reasonable to do so
So, your "helper" method should look like this:
private static String clean(String s) {
return s.toLowerCase().replaceAll("[^a-z]", "");
}
This method does everything your method does, but in a fraction of the code.
Because your main method is also stateless, it too should be static, unless it is required to be an instance method because of class hierarchy or interfaces etc.
Thus, your main method should be:
public static boolean isPalindrome(String s) {
return isPalindromeClean(clean(s));
}
private static boolean isPalindromeClean(String s) {
return s.length() < 2 || a.endsWith(s.charAt(0)) &&
isPalindromeClean(l.substring(1, l.length() - 1));
}
Again, one line of code does it all. Some things to note:
your code calls the "clean" method every recursion, but by creating a second method, I avoid this inefficiency.
the use of endsWith() to both simply and clarify the condition
the use of a single simple return statement that encapsulates the logic
The whole class becomes the following, with just 3 lines of actual code.
public class Recurse {
public static boolean isPalindrome(String s) {
return isPalindromeClean(clean(s));
}
private static boolean isPalindromeClean(String s) {
return s.length() < 2 || a.endsWith(s.charAt(0)) &&
isPalindromeClean(l.substring(1, l.length() - 1));
}
private static String clean(String s) {
return s.toLowerCase().replaceAll("[^a-z]", "");
}
}
I wouldn't bother even having the clean method - I would simply in-line it like this:
public static boolean isPalindrome(String s) {
return isPalindromeClean(s.toLowerCase().replaceAll("[^a-z]", ""));
}
But if you've been set an assignment that says you have to create it then you're stuck with it. I would show this alternative though.
Usually, the more elegant the code, the less there is of it.
Related
I want to remove every second character from a string using the recursive method.
public static void main(String[] args) {
System.out.println(everySecond("wonderful"));
}
public static String everySecond(String s) {
if (s.length() % 2 != 0 ) {
System.out.print(s.substring(0,1));
}
if (s.length() <= 1) {
return s;
}
else {
String simpler = everySecond(s.substring(1))+ s.charAt(0);
return "";
}
}
}
Currently the program does what I need.
However, I want to include everything in the sub-recursive call String simpler = everySecond(s.substring(1))+ s.charAt(0); return ""; and remove code below.
if (s.length() % 2 != 0 ) {
System.out.print(s.substring(0,1));
}
I am fairly new to Java so I apologize if the answer is obvious. I assume I am overlooking some very basic solution here.
If the remaining length of the String is < 2 then we don't need to find any more characters to skip over. Otherwise, we need the initial character and then the rest of the String after the second character(the skipped one), therefore I did it like this:
public static String removeEverySecondChar(String str) {
if(str.length() < 2) {
return str;
}
return str.substring(0,1) + removeEverySecondChar(str.substring(2));
}
Input: Wonderful
Output: Wnefl
So, the task is to create a string that makes a progression throughout the letters of a string, returning a substring progressively longer.
For example if the input is Book, the answer would be: BBoBooBook . For the input Soup the method would return SSoSouSoup. I want to write it recursively. In my current method I receive no error but at the same time no anwer from the compiler.
public static String stringProgression(String str) {
int index = 1;
String result = "";
if (str.length() == 0) {
return "" ;
} else while (index <= str.length()); {
result = result + stringExplosion(str.substring(0, index));
index++;
}
return result;
}
In your code, you are using two different method names, stringProgression and stringExplosion.
Further, you have a while loop with a semicolon, while (index <= str.length()); which forms an empty loop. Since index doesn’t change in this empty loop, it will be an infinite loop when the condition is fulfilled.
Generally, a while loop contradicts the intent to have a recursive solution.
To find a recursive solution to a problem, you have to find the self-similarity in it. I.e. when you look at the intended result for Book, BBoBooBook, you can recognize that the beginning, BBoBoo is the right result for the string Boo, and BBo is the right result for Bo. So, the original string has to be appended to the result of a recursive evaluation of the substring:
public static String stringProgression(String str) {
if(str.isEmpty()) {
return str;
}
return stringProgression(str.substring(0, str.length() - 1)) + str;
}
An alternative, shorter syntax for the same is:
public static String stringProgression(String str) {
return str.isEmpty()? str: stringProgression(str.substring(0, str.length() - 1)) + str;
}
Check this one:
private static String doStringProgression(String str, String res, int length) {
if(length > str.length()) {
return res;
}
return doStringProgression(str, res + str.substring(0, length), length + 1);
}
And you can call the method with input like in the following example:
public static String stringProgression(String str) {
return doStringProgression(str, "", 1);
}
Given a string, compute recursively (no loops) a new string where all appearances of "pi" have been replaced by "3.14".
changePi("xpix") → "x3.14x"
changePi("pipi") → "3.143.14"
changePi("pip") → "3.14p"
My code worked perfectly but is there any other way (only recursively no loops) to do this problem without having to create a new string str2 ?
Thank you in advance
here is my code :
public String changePi(String str) {
String str2 = "";
return changePi(str, str2);
}
public String changePi(String str, String str2) {
if (str.length() == 0)
return str2;
else {
if (str.endsWith("pi")) {
str2 = 3.14 + str2;
return changePi(str.substring(0, str.length() - 2), str2);
} else
str2 = str.charAt(str.length() - 1) + str2;
}
return changePi(str.substring(0, str.length() - 1), str2);
}
Using the same mechanism you can use a StringBuilder and modify it in-situ. This should be much more memory efficient.
private static final String PI = "pi";
private static final String THREE_POINT_ONE_FOUR = "3.14";
public String changePi(String s) {
// Work with a StringBuilder for efficiency.
StringBuilder sb = new StringBuilder(s);
// Start replacement at 0.
return changePi(sb, 0).toString();
}
private StringBuilder changePi(StringBuilder sb, int i) {
// Long enough?
if (i + PI.length() <= sb.length()) {
// Is it there?
if (sb.subSequence(i, i + PI.length()).equals(PI)) {
// Yes! - Replace it and recurse.
sb.replace(i, i + PI.length(), THREE_POINT_ONE_FOUR);
return changePi(sb, i + THREE_POINT_ONE_FOUR.length());
} else {
// Not there - step to next.
return changePi(sb, i + 1);
}
}
return sb;
}
private void test(String s) {
System.out.println(s + " -> " + changePi(s));
}
private void test() {
test("pipi");
test("xpix");
test("pip");
}
You are doing the same mistake as you did in your previous question. Also I would prefer to check if a string starts with, and not ends with a string...I am assuming that you would like something that you can understand and it's easy to explain.
Can you match "pi" or the string is already less than length("pi") symbols -> cant do nothing much so return it.
Does it starts with "pi"? If so return the replacement concatenated with the rest of the string (just the rest starts length("pi") characters away from the 0th index...
If it isn't starting with "pi" than concatenate the first character with what's the output of changePi and the rest of the string as its input.
public static String changePi(String str) {
if (str.length() < "pi".length()) {
return str;
}
if (str.startsWith("pi")) {
return "3.14" + changePi(str.substring("pi".length(), str.length()));
}
return str.charAt(0) + changePi(str.substring(1, str.length()));
}
And still if you like to use the "endsWith" logic then here is the same algorythm applied.
public static String changePi(String str) {
if (str.length() < "pi".length()) {
return str;
}
if (str.endsWith("pi")) {
return changePi(str.substring(0, str.length() - "pi".length())) + "3.14";
}
return changePi(str.substring(0, str.length() - 1)) + str.charAt(str.length() - 1);
}
public static String changePi(String str) {
int num = str.indexOf("pi");
if (num==-1) return str;
return str.substring(0,num)+"3.14"+changePi(str.substring(num+2));
}
does it, though I guess it depends what you mean by create a new string. In a sense this does, but without naming it.
Your solution is a nice approach (it's a little confusing as you replace twice, I believe) and I would make sth like:
If str <= than 2, check str if equals pi, return 3,14 or str
Else, if ends with pi return Change(str-2)+Change(last2), else return Change(str-1)+Change(lastChar)
Edited: now changes pi for 3,14
Voila! simple and clean:
String fun(int i) {
if(i == s.length()) return "";
if(i+1 < s.length() && s.charAt(i) == 'p' && s.charAt(i+1) == 'i') {
return "3.14" + fun(i+2);
} else {
return s.charAt(i) + fun(i+1);
}
}
Any reason why you have not considered using regular expressions?
The following seems to work fine for all the test cases you mentioned:
public String changePi(String str) {
return str.replaceAll("pi", "3.14");
}
Note that in Java a String is immutable. You cannot modify a String once you initialise it. Each time you are doing str2 = ... you are really creating a completely new String object.
If you need to do it recursively (i.e. it is some coursework), then what you did is perfectly fine (although I would have used indexOf("pi") rather than endsWith("pi") and removing the trailing characters, but anyway.
It is the standard way to perform recursion, with str2 being the accumulator (maybe you want to rename it so that it is clear what it is doing). You might want to consider using StringBuilder instead of a String for the second parameter, and in your base case you call toString() to get the final string... although honestly the difference in computation cost between creating a new String and using a StringBuilder has become very low.
I've never written Java before, but this seems to work:
public class HelloWorld {
public static void main(String[] args) {
System.out.println(changePi("xpix"));
System.out.println(changePi("pipi"));
System.out.println(changePi("pip"));
}
public static String changePi(String str) {
int len = str.length();
if (len < 2)
return str;
if (str.endsWith("pi"))
return changePi(str.substring(0, len - 2)) + "3.14";
else
return changePi(str.substring(0, len - 1)) + str.substring(len - 1, len);
}
}
Explaination:
This is closer to a true recursive solution, since the function doesn't know about the right portion of the string (no str2), it's the calling function's responsibility to keep track of it.
The terminal case is running on a string shorter than "pi", in which case we return the remainder (0 or 1 chars).
If the string ends in "pi", we take the rest of the string before that and pass that to the new iteration, but keep in mind to append "3.14" when that returns.
If the string doesn't end in that, we just break off a single character and keep it to reattach.
Criticism:
This is horribly inefficient, though, as every .substr call is creating a whole new string object, and we do a lot of these calls.
You would have better luck using something like a linked list of characters.
public String changePi(String str) {
if(str.length() == 0){
return str;
}
if(str.startsWith("pi")){
return "3.14" + changePi(str.substring(2));
}
return str.substring(0,1) + changePi(str.substring(1));
}
public class ChangePi {
public static String changePi(String str) {
String pi = "3.14";
if (str.length() < 2) {
return str;
}
return (str.substring(0, 2).equals("pi")) ? pi + changePi(str.substring(2)) :
str.substring(0, 1) + changePi(str.substring(1));
}
}
Hope my answer explains everything :)
public String changePi(String str) {
if (str.length() < 1) return ""; // return if string length is zero
String count = str.substring(0,1); // get the string
int increment = 1; // increment variable to iterate the string
if (str.length() > 1 && str.substring(0, 2).equals("pi")){
//string length > 1 and it pi if found
count = "3.14";
increment = 2;//increment by 2 characters
}
//attach the 3.14 part or substring(0,1) and move forward in the string
return count + changePi(str.substring(increment));
}
Here is the answer:
public String changePi(String str) {
if(str.equals("")){
return str;
}
else if(str.length()>=2 && str.charAt(0)=='p' && str.charAt(1)=='i') {
return "3.14" + changePi(str.substring(2));
}
else{
return str.charAt(0) + changePi(str.substring(1));
}
}
public String changePi(String str) {
if(str.length() == 0) return str;
if(str.charAt(0) == 'p' && str.length() >= 2){
if(str.charAt(1) == 'i'){
return "3.14" + changePi(str.substring(2));
}
}
return str.charAt(0) + changePi(str.substring(1));
}
So I have a public static method 'getWithoutLeadingZeroes', which gets passed a String and simply needs to return it without any zeroes prefixing the string of numbers.
Now, I know that I need to iterate through the string until I find the first non-zero char in the string, but I'm not exactly sure how to take the point where the method finds the non-zero char and start copying the remainder of the String into a new String, then returning it.
Here's what I have so far:
public static String getWithoutLeadingZeroes(String s) {
boolean notZero = false;
char[] t = new char[x];
for(int i = 0; i<s.length(); i++){
if(s.charAt(i) == 0){
notZero = false;
} else {
notZero = true;
}
if(notZero = true){
for(int j = index.charAt(i))
}
return ""; //to be completed
}
I created a boolean variable to stop the loop once it hits the non-zero char and I'm pretty positive the first half of the code is accurate, but its the creating of the new String to be returned that I'm a bit stuck on. Any suggestions would be welcome.
You are NOT stopping your loop.
You could do that using the break keyword.
And: your comparison is wrong, it should read
if (... == '0'
You could use String#substring(int beginIndex) instead:
public static String getWithoutLeadingZeroes(String s) {
int i = 0;
while (i < s.length() && s.charAt(i) == '0') i++;
return s.substring(i);
}
How about the following way?
public static String getWithoutLeadingZeroes(String s) {
while(s.startsWith('0'))
s = s.substring(1);
return s;
}
This should do it
String nmbrStr = "1001234";
String cleanedStr = nmbrStr; // this way the whole number will be
// returned if in has no leading zeroes
for (int i = 0; i < nmbrStr.length(); i++) {
if (nmbrStr.charAt(i) != '0') {
cleanedStr = nmbrStr.substring(i);
break;
}
}
System.out.println(cleanedStr);
You can also use Integer.parseInt(nmbrStr) which is a lot cleaner
A simple solution.
public static String getWithoutLeadingZeroes(String stringOfNumbers) {
return String.valueOf(Long.parseLong(stringOfNumbers));
}
Another alternative solution:
public String getWithoutLeadingZeroes(String str) {
int from = 0;
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i) == '0'){
from = i;
}else{
break;
}
}
return str.substring(from+1);
}
You can then make it a little bit more robust by implementing try/catch blocks and possibly along with cases that deal with unexpected input e.g null parameter or a zero-length string etc.
I want to count number of words in my string using recursive method (java)
so far i wrote this code
public static int CountWords(String sen) {
int count = 0;
int i = sen.indexOf(" ");
if (sen.isEmpty()) {
return 0;
}else
if (i == sen.indexOf(" ")) {
return count++;
}
//sen.substring(0,sen.indexOf(" ")-1);
count++;
return count + CountWords(sen.substring(i + 1));
}
i always get 0 when i call the method
can anyone help me make this code run
How you're using indexOf is the problem. You're setting i to the result of calling indexOf, then seeing if it's equal to the result of calling indexOf on the same string with the same parameter. The result of the test i == sen.indexOf(" ") will always be true. That's why you always get 0.
String#indexOf returns -1 if the char it's looking for is not found. indexOf comes in very handy here.
Also you shouldn't need a local count variable. Introducing a variable here just makes the code harder to read, because the reader has to hunt around to figure out what the value of it is.
Assuming your input always has exactly one blank between words this could be done as:
public static int countWords(String s) {
if (s.isEmpty()) return 0;
if (s.indexOf(" ") == -1) return 1;
return 1 + countWords(s.substring(s.indexOf(" ") + 1));
}
For multiple blanks between words you can check for a blank and skip past it:
public static int countWords(String s) {
if (s.isEmpty()) return 0;
if (s.indexOf(' ') == -1) return 1;
if (s.charAt(0) == ' ') return countWords(s.substring(1));
return 1 + countWords(s.substring(s.indexOf(' ') + 1));
}
This should work, I think:
public static int countWords(String sen) {
int i = sen.indexOf(" ");
if (sen.isEmpty()) {
return 0;
} else if (i == -1) {
return 1;
} else return 1 + countWords(sen.substring(i + 1));
}
Some notes on what is happening:
Java naming conventions dictate you should start method names with a lower case letter
The line if (i == sen.indexOf(" ")) is redunant - you just assigned i to be that before, so it'll always evaluate to true.
And therefore, your recursion never gets called. You need to change it so that if sen isn't empty and contains at least one more space, countWords calls itself with sen minus the first word.
This method uses a String with no spaces as a base case. Then it removes everything up to and including the first space in the String and recurses.
It handles both the special case of an empty String and the case that a String passed to the method starts with a space appropriately.
public static int CountWords(String sen)
{ int i = sen.indexOf(" ");
if(sen.isEmpty()) return 0; // special case
if(i == -1) return 1; // base case
if(i != 0)
return 1 + CountWords(sen.substring(i+1));
else
return CountWords(sen.substring(1));
}
This will work -
public static int CountWords(String sen) {
if("".equals(sen)){
return 0;
}
int count = 0;
int i = sen.indexOf(" ");
String substr = sen.substring(0,i+1) ;
if (i != -1) {
count++;
}else{
if(sen.length()>0){
count++;
}
sen="";
}
//sen.substring(0,sen.indexOf(" ")-1);
return count + CountWords(sen.substring(substr.length()));
}