I've seen lots of posts about reversing a string with recursion, but I like to use my own style of coding to properly understand it. Anyway here is the code I have.
private static String reverse (String s){
String rev = "";
int index = s.length()-1;
if(index >= 0){
rev += s.charAt(index);
index--;
rev += reverse(rev);
}
return rev;
}
Basically index just keeps going for every single character when index is -1 the loop stops. it reads the last possible character of the string but there is an error in this line here.
rev += rev(str)
Here is iterative method
String dog = "dog";
String rev = "";
int index = dog.length()-1;
while(index >= 0){
rev+=dog.charAt(index);
index--;
}
out.println(rev);
I believe when the method calls itself you reset the value of the index. Maybe a better approach would be to have an overloaded method where the original call passes in the string to process and then the remaining calls call a method where you pass in both the string you are building along with the remaining part of the that is being processed.
The recursive step doesn't contain the mutated string. You need to pass the substring of the original string.
rev = reverse(s.substring(0,index));
(Posted answer on behalf of the OP).
It's fixed now. Thank you so much, I was indexing too late and passing in negative numbers.
private static String reverse (String s) {
int index = s.length();
index--;
String rev = "";
if(index >= 0){
rev += s.charAt(index);
rev = rev + reverse(s.substring(0,index));
}
return rev;
}
Related
I'm trying to concatenate a string with itself and remove all capital letters from the resultant string.
Here is my code:
public String removeCapitals(String A) {
StringBuilder B = new StringBuilder(A+A);
int n = B.length();
for(int i=0; i<n; i++){
if(B.charAt(i)>='A' && B.charAt(i)<='Z'){
B.deleteCharAt(i);
}
}
return B.toString();
}
I'm getting Exception saying:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6
at java.lang.AbstractStringBuilder.charAt(AbstractStringBuilder.java:237)
at java.lang.StringBuilder.charAt(StringBuilder.java:76)
at Solution.removeCapitals(Solution.java:10)
at Main.main(Main.java:190)
Can someone help me to understand the issue.
If at least one removal succeeds, at some point your code will attempt to access an invalid index that exceeds the length of a StringBuilder.
It happens because the variable n remain unchanged. You should change the condition to be bound to the current size of StringBuilder and decrement the index at each removal, or iterate backwards (as shown in another answer).
Also condition B.charAt(i)>='A' && B.charAt(i)<='Z' can be replaced with:
Character.isUpperCase(b.charAt(i))
Which is more descriptive.
That's how it might look like:
public static String removeCapitals(String a) {
StringBuilder b = new StringBuilder(a + a);
for (int i = 0; i < b.length(); i++) {
if (Character.isUpperCase(b.charAt(i))) {
b.deleteCharAt(i); // which can be combined with the next line `b.deleteCharAt(i--);` - previous value of `i` would be used in the call `deleteCharAt()` and variable `i` will hold a value decremented by 1
i--;
}
}
return b.toString();
}
Method deleteCharAt() runs in a linear time, because it shifts all subsequent characters in the underlying array bytes. Each upper-case letter will trigger these shifts and in the worst case scenario, it would result in the quadratic overall time complexity O(n ^ 2).
You make your method more performant and much more concise without using loops and StringBuilder. This code will run in a linear time O(n).
public static String removeCapitals(String a) {
return a.replaceAll("\\p{Upper}", "").repeat(2);
}
When you delete a character you change the length of the StringBuilder. But n still has the original length. So you will eventually exceed the size of the StringBuilder. So start from the end and move backwards. That way, any deletions will come after (based on relative indices) the next position so the index will be within the modified StringBuilder size. In addition, deleting from the end is more efficient since there is less copying to do in the StringBuilder.
public String removeCapitals(String A) {
StringBuilder B = new StringBuilder(A+A);
int n = B.length();
for(int i=n-1; i>=0; i--){
if(B.charAt(i)>='A' && B.charAt(i)<='Z'){
B.deleteCharAt(i);
}
}
return B.toString();
}
If just remove Capital characters from a string. Alternative solution just create another method replaceAll() + regex
private static String removeCapitals(String A){
if (!A.isEmpty() && !A.equals("")) {
String B = A + A;
String newStr = B.replaceAll("([A-Z])", "");
return newStr;
} else {
return null;
}
}
Shorter solution to your task.
String a = "ABcdEF";
String b = "";
for (int i = 0; i < a.length(); i++) {
if(a.toLowerCase().charAt(i) == a.charAt(i))
b+=a.charAt(i);
}
System.out.println(b);
By changing to .toUpperCase() you'll get rid of the lower case ones.
I need to write a function that receives a String and removes adjacent duplicates.
Example:
Input -> "aabbaabbcccaaa"
Output -> "ababca"
I tried to solve it as follows:
public String remdups(String input) {
String response = "";
char temp;
int i, length = input.length();
for(i = 0; i < length; i++) {
temp = input.charAt(i);
response += temp;
while(i < length && input.charAt(i) == temp) i++;
}
return response;
}
But it seems that time complexity is not as expected, how could I improve perfomance or what would be a better approach?
I know it's a really simple problem, but I can't find a way to improve or another way to do it.
To me your code looks already good from a complexity point of view. It is only going through the String once. The optimisations you could do are on the response String by using a StringBuilder, and maybe simplifying the loop a bit just for readability (no need for 2 nested loops, and incrementing the i counter from 2 places could introduce mistakes).
public String remdups(String input) {
StringBuilder response = new StringBuilder(input.length());
char temp;
for (int i = 0; i < input.length(); i++) {
char next = input.charAt(i);
if (temp != next) {
temp = next;
response.append(temp);
}
}
return response.toString();
}
Why not try something with regex? Like so:
public static void main(String[] args) {
String str = "aabbaabbcccaaa";
System.out.println(str.replaceAll("(.)\\1+","$1"));
}
Output:
ababca
Edit:
For future reference, this approach turns out to be abysmally slow. I benchmarked it with JMH and it’s approximately 4x slower than the non-regex solution for short strings, and only gets worse for longer (~10k character) strings.
I'm trying to use recursion to find the reverse of a string, but I get a stackoverflowerror when I run my code. I'm new to recursion so I'm not sure what I need to do to fix it.
public static String reverse(String string) {
int index = 0;
if(string == null){
return " ";
}
else if(index < string.length()) {
char a;
a = string.charAt(index);
index += 1;
return a + reverse(string);
}
return " ";
}
This is not how recursion should work, because you are just passing the same string over and over again. You could use recursion, but there are two approaches for your problem.
Get the last character and call the method with a string that doesn't have the last character, like so:
public String reverse(final String string) {
if (string != null && !string.isEmpty()) {
final int length = string.length();
final char character = string.charAt(length - 1));
final String result = reverse(string.substring(0, length - 2));
if (result != null)
return String.valueOf(character) + result;
return String.valueOf(character);
}
return null;
}
I should not that I have not tested this, but the point is that I am changing the string passed and have a mechanism to detect when to quit calling my own method.
The second method is to do this without recursion, because you can accomplish this with some for loops and such. But for the sake of learning, check 1 :P
There are a number of problems. I've added some comments to try to help.
public static String reverse(String string) {
int index = 0;
if(string == null){
return " ";
}
/* This will always be true because index is always zero */
else if(index < string.length()) {
char a;
/* This gets the character at position zero */
a = string.charAt(index);
/* This increments zero by 1 */
index += 1;
/* This takes the character and then calls reverse again (the recursion.
The problem is that you are passing the full string in and so every time
you recurse, index will be zero again and the string will be exactly the same length.
So no exit criterion will ever be triggered.
As such this is an infinite recursion. */
return a + reverse(string);
}
return " ";
}
I would suggest considering the following:
On the last recursive call, i.e. when you start to "pop back" through each of the recursive calls, what is it that you expect to trigger this?
If you decide that on every recursive call you are going to shorten the string by removing the first character, then the last call would be an empty string.
On the other hand, if you decide that your last call will be that the index variable equals the string length, then you will want to consider passing in an extra parameter (the index) and increasing this by one on every recursive call.
The piece you seem to be missing is passing the index into your function. Also, you need to return the current character at the end when you recurse. I think you wanted something like
private static String reverse(String string, int index) {
if (string != null && index < string.length()) {
char a = string.charAt(index);
return reverse(string, index + 1) + a;
}
return "";
}
Then your method in the public interface can call that function with an initial index of 0 like
public static String reverse(String string) {
return reverse(string, 0);
}
Of course, in real code I would prefer StringBuilder and something like
public static String reverse(String string) {
StringBuilder sb = new StringBuilder(string);
return sb.reverse().toString();
}
You're initializing variables "index and a" each time the recursive method get called. Initialize all of the variable outside the method block.
Try this function..
public static String reverse(String str){
if(str.length()<=0||str.length()==1)
return str;
return reverse(str.substring(1))+str.charAt(0);
}
I wrote a method that loops through a string and adds '/n' to create a line length that was given in the parameters. That description is not the best but it's hard to describe so look at the code below. Thanks in advance!
My Code:
public static String lineLength(String str, int length){
int totalLength = 0; //total length of the document
int lengthConst = 0; //constant value of the length for the loop
int nLength = 0; // length of \n = 2 characters
String work1, work2; //Strings to work with in the loop. Used as string buffers in substrings
if(str != null){
totalLength = str.length();
lengthConst = length;
}
if(length < 1){
throw new NullPointerException("Length must be >= 1");
}
/*
Main Loop: check again if length is not zero, check if totalLength is not zero,
check if pseudoCursor is not zero, check if length is less than or equal to totalLength
*/
while((length != 0) && (totalLength != 0) && (lengthConst != 0) && (length <= totalLength)){
work1 = str.substring(0, length); //store string of beginning to line length
work2 = str.substring(length + nLength, str.length()); //store string from length to end
work1 = work1.concat("\n"); //add new line
str = work1.concat(work2); //add work1 and work2 and store in str
nLength += 1; //nLength increases by 2 because we are going to add another \n
length += length;
}
return str;
}
When provided with the string "Daniel" and the new line length of 2 this is the run when printed to the console:
run:
Da
n
el
BUILD SUCCESSFUL (total time: 4 seconds)
I'd recommend using a for loop. I think it would be easier than what you are currently doing. Generally for loops go as such:
for(START POSITION, CONTROL, ITERATION PATTERN){
CODE
}
I'd read more about for loops here:
http://www.tutorialspoint.com/java/java_loop_control.htm
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
The String object has a method .length() which will be used for the control of the loop. You want to iterate by 2 (because that's how you're separating it the words). You also want to start at 1 (usually the starting position is 0 but in this case we want 1):
String word = "Daniel";//starting word
String outputWord = "";//make it empty quotes so you can concatenate to it.
//if the size of word is less than 2, then print so
//else do the code below
for(int i = 1; i < word.length(); i = i+2){
outputWord = outputWord + word.get(i-1) + word.get(i) + "\n";
}
//check if the length was of word was odd. if so, then add the last character to outputWord
System.out.println(outputWord);
NOTE: This will only working assuming your word variable is at least 2 in size. I'll leave that error handling up to you to write. You'll also want to handle in odd length cases as well.
Here's a much simplified version
public static String lineLength(String str, int length) {
StringBuilder sb = new StringBuilder();
while(true) {
if(str.length() <= length) {
sb.append(str);
break;
}
sb.append(str.substring(0, length));
sb.append("\n");
str = str.substring(length);
}
return sb.toString();
}
You still need to understand what was wrong with your solution so that you learn from it and can apply that knowledge to the code you write in the future. Step through both this and your original code in a debugger and observe carefully what is happening.
public String starString(int n){
int m = (int)Math.pow(2,n);
String str="";
str = starString(m-1,str);
return str;
}
private String starString(int n, String str){
String temp ="";
if (n<0) {
try{
throw new IllegalArgumentException();
}
catch(IllegalArgumentException ex){
}
}
else {
temp+=("*");
starString(n-1,str);
}
return temp;
}
Can someone please explain to me why this code returns a single asterisk even if its called by a value greater than n >= 0?
I debugged and noticed that after throwing the exception it recurses again and all the asterisks get chopped to "". I've tried it many times. Its also required that you should throw the IllegalArgumentException if n < 0.
In Java strings are immuntable, hence you need to assign a new value to temp (and pass temp as the parameter):
temp = starString(n-1, temp);
Additionally you'd need to assign str to temp, otherwise each recursion would just return a single asterisk:
String temp = str;
A much simpler, cleaner (and correct) version of your recursive method would look like this:
private String starString(int n){
String temp = "*";
//only recurse as long as n > 0, i.e. the last invocation would be made with n = 0
if (n > 0){
temp += starString(n-1);
}
return temp;
}
Note that you don't even need to pass the string as a parameter. Also note that recursion is overkill here, using a loop would make much nore sense. Also note that string concatenation is costly and gets slow quickly for higher values of n (due to immutable string instances being created over and over again). In that case you'd better use StringBuilder:
private String starString(int n){
StringBuilder s = new StringBuilder();
for( int i = 0; i <= n; i++ ) {
s.append("*");
}
return s.toString();
}
On my machine a loop version using string concatenation takes around 12 seconds for n = 100000 whereas the StringBuilder version takes 0.007 seconds.
Your code invokes every recursion, stores a local temp, returns this and it is never used.