Why does this code work?
public static String reverse(String a) {
if(a.length() == 0) {
return a;
} else {
return reverse(a.substring(1)) + a.charAt(0);
}
}
And this doesn't?:
public static String reverse(String a) {
if(a.length() == 0) {
return a;
} else {
return reverse(a.substring(1)) + a.substring(0);
}
}
Also, how does the recursion work in case 1, what does adding a.charAt(0) do? And how does this method ever reach the base case?
Because a.charAt(0) returns the first character, while a.substring(0) returns the entire String (from index 0). Change
return reverse(a.substring(1)) + a.substring(0);
to something like
return reverse(a.substring(1)) + a.substring(0, 1);
And it will work as expected.
To get better understanding about recursive code, you can try to print the state for each method calls, e.g.
public static String reverse(String a) {
System.out.println("Calling reverse(\"" + a + "\")");
if(a.length() == 0) {
System.out.println("Base case encountered for string : \"" + a + "\"");
return a;
} else {
String b = reverse(a.substring(1));
String c = a.charAt(0);
System.out.println(reverse(\"" + a + "\") returning \"" + b + "\" + \"" + c + "\"");
return b + c;
}
}
When you try to call reverse("xyz"), then you can see the following text printed within standard output:
Calling reverse("xyz")
Calling reverse("yz")
Calling reverse("z")
Calling reverse("")
Base case encountered for string : ""
reverse("z") returning "" + "z" = "z"
reverse("yz") returning "z" + "y" = "zy"
reverse("xyz") returning "zy" + "x" = "zyx"
We can see several things:
You reduce the string recursively until reaching the base case where the string is empty (has zero length).
For each non base case, you split the string into two segment, namely b and c. Then you return reverse(b) + c.
Firstly, 'a.substring()' returns the substring starting from the index given as parameter, so while using 'a.substring(1)' as the parameter of the recursive method the first character always gets skipped and the length of the string given as parameter decreases gradually. Once no character remains it reaches the base case.
Secondly, 'a.charAt()' returns the character exists at the index of the string given as parameter. So 'a.charAt(0)' returns the first index of the string 'a' which is given as the parameter of the recursive method.
Finally, the first code works because each time it sends the entire string except the first character and it includes that first character at the end of the string that is returned reversed. So at the end, the entire string gets reversed.
On the other hand, the second code includes the entire substring starting from the first index of the string given as its parameter instead of the first character.
To make the code work you can either use 'charAt(0)' like the first code -
return reverse(a.substring(1)) + a.charAt(0);
or you can use 'a.substring(0, 1)' which considers only the first character as the substring and returns it -
return reverse(a.substring(1)) + a.substring(0, 1);
Related
While auditing an android source code, I found a string comparison bug which used == instead of equals(). However, the app is working well surprisingly!
After some testing, I found that replaceAll() method is hiding the bug.
String description = " ";
description = description.trim();
Result1.setText(description + " == " + "" + ": " + (description == ""));
prints "==:false" as I expected. However,
String description = " ";
description = description.trim().replaceAll("\\s+|\\r+|\\n+", " ");
Result1.setText(description + " == " + "" + ": " + (description == ""));
prints "==:true"! (Android 4.4.2, API 19)
I run the same code in my desktop (javac 1.6.0_45) and it prints "==:false" as I expected.
Is it a bug in Android or is it intended behavior?
No, it's not a bug -- it's just an implementation detail leaking out.
The java compiler creates a pool of strings used in the code. The empty string is surely one of these. Any time you set a variable to the empty string at compile-time, it will point to the same instance of the empty string. So
String a = "";
String b = "";
if (a == b) {
//this will always be true in practice, although I don't know if it's guaranteed
}
Now imagine that trim() and replaceAll() are implemented differently:
String trim() {
byte[] b = getBytes();
...
return new String(b, 0, len);
}
String replaceAll (String needle, String replacement) {
String result = "";
int pos = 0;
while (indexOf(needle, pos) != -1) {
...
result = result + replacement;
pos = ...;
}
return result;
}
Because trim() calls a String constructor, it necessarily creates a new String. But replaceAll starts with an empty String and builds up. And the empty String that it starts with is the same empty string as all the other empty strings in your source code.
These are fake implementations -- it's just a hypothesis that this is how it works. It matches the observed data, but I didn't read the Android code. Still, it demonstrates that different implementations of similar functions could lead to the effect that you're seeing.
In other words, it's not a bug, but it's not a behavior you want to depend on. If you do want to depend on two strings that .equal() also being ==, you can use String.intern().
I believe that I have a decent understanding of recursion (factorial etc), however in the following example in reversing a string I do not understand the line. Can someone please explain what it does?
return reverseString(str.substring(1)) + str.charAt(0);
Full Code from method:
public static String reverseString(String str){
if(str.length()<2){
System.out.println("reached Base case");
return str;
}
return reverseString(str.substring(1)) + str.charAt(0);
}
The call substring(1) takes the first character off of the string. That is fed into the recursive call, which reverse all but the last character. Then, the first character is appended, completing the reversal.
Example:
reverseString("abc") =>
reverseString("bc") + 'a' =>
(reverseString("c") + 'b') + 'a' =>
("c" + 'b') + 'a' =>
"cb" + 'a' =>
"cba"
It takes everything after the first character and calls the recursive function. The first character is put at the end of the string. This results in a reversal.
return reverse(Everything after the first) + the first
return reverseString(str.substring(1)) + str.charAt(0);
For example, the String is HelloWorld
Then "HelloWorld".substring(1) returns "elloWorld"
and "HelloWorld".charAt(0) returns "H"
You take the first letter and add it to the end od the string. But before, you do it again with the first part. In the end, this algorithm reverses the string.
Let's take this string:
str = "Reverse";
The value of str.substring(1) is "everse".
The value of str.charAt(0) is "R".
I think you can take it from there if you understand recursion.
It inefficiently reverses the string by placing each successive sub-string on the stack until it reaches a length less then 2; it then reverses the characters by popping those results off the stack and appending the first character to the sub-string. It is inefficient because Java includes the StringBuilder class and that has a reverse method.
Try to think of reversing a string in this manner:
//reverse of a string can be expressed as the last character
//plus the reverse of everything remaining. For example, if we had
//"food", you would have "d" + reverse("foo"), which is "d" + "oof"
//which gives you "doof". So:
reverse(str) = str[str.length - 1] + reverse(str[0:str.length - 2]);
reverse(str[0]) = str[0] //reverse of a 1 character string is the character itself
So apply this to the string abcd:
You have:
reverse("abcd") = "d" + reverse("abc")
reverse("abc") = "c" + reverse("ab")
reverse("ab") = "b" + reverse("a")
reverse("a") = "a";
Now when you substitute you have:
reverse("ab") = "b" + "a" = "ba"
reverse("abc") = "c" + "ba" = "cba"
reverse("abcd") = "d" + "cba" = "dcba"
Think about how you can write code that mimics this behavior.
I have two simple examples to support my question. I can't figure out why (1) is working while (2) isn't. In my opinion I use them the same way.
(1)
public String frontBack(String str) {
if (str.length() <= 1) return str;
String mid = str.substring(1, str.length()-1);
// last + mid + first
return str.charAt(str.length()-1) + mid + str.charAt(0);
}
(2)
public String front22(String str) {
str = "test";
return str.charAt(0);
}
With the second one, I get an type mismatch error that says: Cannot convert from char to string. When I try to find an answer on internet I see the str declared as a var type in all examples. But it works with the first example.
What am I missing?
In the first example you return a String. In the second you (try to) return a char.
Since you do string concatenation in the first example the result of the expression is a string.
To return the first character as a String:
return str.substring(0,1);
You can fix it by typing
return "" + str.charAt(0);
Somehow that forces the character into a string.
So I'm having a little trouble with the final part of my assignment on recursion. The method needs to use recursion to return a string that is formed from "weaving" together two strings taken in as parameters. For example:
weave("aaaa", "bbbb") // should return the string "abababab"
weave("hello", "world") // should return the string "hweolrllod"
weave("recurse", "NOW") // should return the string "rNeOcWurse"
Note that the extra characters from the first string—the characters in "urse"—come after the characters that have been woven together.
The important (and annoying) thing is that I'm not allowed to use any iteration loops (for, while, do while).
Here is my code thus far:
public static String weave(String str1, String str2)
{
String word = str1 + str2;
if(str1 == null || str1.equals("") || str2 == null || str2.equals(""))
{
return word;
}
String word1 = weave(str1.substring(0, str1.length() - 1), str2.substring(0, str2.length() - 1));
System.out.println(word1);
return word;
}
For (Hello, World), my output is:
HW
HeWo
HelWor
HellWorl
HelloWorld
Obviously my characters aren't weaving, so I'm not sure what to do! Also, as stated above, the method should do not printing. I just added in the println statement as a test to see where my program was at.
I think something like the following might work.
public String weave(String str1, String str2)
{
if(str1.isEmpty() || str2.isEmpty()) {
return str1 + str2;
}
return str1.substring(0, 1) + str2.substring(0, 1) + weave(str1.substring(1), str2.substring(1));
}
The idea is pretty simple: you only need to pop the first character from both input strings and concatenate both characters and the returned value from calling the function recursively with the stripped input strings until one of the input strings is empty in such case you should merely return the non-empty string.
weave("abcdef", "12"): "a" + "1" + weave("bcdef", "2")
|
+- weave("bcdef", "2"): "b" + "2" + weave("cdef", "")
|
+- weave("cdef", ""): "cdef"
Resulting in:
weave("abcdef", "12"): "a" + "1" + "b" + "2" + "cdef": "a1b2cdef"
The problem with your code is:
String word = str1 + str2;
//...
return word;
No matter how the recursive call, at the end it just return the result from the first method call when you pass in "hello", "world".
String word = str1 + str2; //hello + world
//... other things and the recursive call doesn't matter
//return word; //return the first word variable which is helloworld
I want to concatenate a string before the last occurrence of any character.
I want to do something like this:
addToString(lastIndexOf(separator), string);
where "ddToString" is a function that would add the "string" before the "lastIndexOf(separator)"
Any ideas?
One way I thought of is making string = string + separator.
But, I can't figure out how to overload the concatenate function to concatenate after a particular index.
You should look in Java's api at http://download.oracle.com/javase/7/docs/api/ and use the String Classes substring(int beginIndex)method after you find the index of your specified character so
public String addToString(String source, char separator, String toBeInserted) {
int index = source.lastIndexOf(separator);
if(index >= 0&& index<source.length())
return source.substring(0, index) + toBeInserted + source.substring(index);
else{throw indexOutOfBoundsException;}
}
Try this:
static String addToString(String source, int where, String toInsert) {
return source.substring(0, where) + toInsert + source.substring(where);
}
You'll probably want to add some parameter checking (in case character isn't found, for instance).
You need to use StringBuffer and method append(String). Java internally converts + between Strings into a temporary StringBuffer, calls append(String), then calls toString() and lets the GC free up allocated memory.
The simple way is:
String addToString(String str, int pos, String ins) {
return str.substring(0, pos) + ins + str.substring(pos);
}