Removing a substring from a string, repeatedly - java

Problem:
Remove the substring t from a string s, repeatedly and print the number of steps involved to do the same.
Explanation/Working:
For Example: t = ab, s = aabb. In the first step, we check if t is
contained within s. Here, t is contained in the middle i.e. a(ab)b.
So, we will remove it and the resultant will be ab and increment the
count value by 1. We again check if t is contained within s. Now, t is
equal to s i.e. (ab). So, we remove that from s and increment the
count. So, since t is no more contained in s, we stop and print the
count value, which is 2 in this case.
So, here's what I have tried:
Code 1:
static int maxMoves(String s, String t) {
int count = 0,i;
while(true)
{
if(s.contains(t))
{
i = s.indexOf(t);
s = s.substring(0,i) + s.substring(i + t.length());
}
else break;
++count;
}
return count;
}
I am just able to pass 9/14 test cases on Hackerrank, due to some reason (I am getting "Wrong Answer" for rest of the cases). After a while, I found out that there is something called replace() method in Java. So, I tried using that by replacing the if condition and came up with a second version of code.
Code 2:
static int maxMoves(String s, String t) {
int count = 0,i;
while(true)
{
if(s.contains(t))
s.replace(t,""); //Marked Statement
else break;
++count;
}
return count;
}
But for some reason (I don't know why), the "Marked Statement" in the above code gets executed infinitely (this I noticed when I replaced the "Marked Statement" with System.out.println(s.replace(t,""));). I don't the reason for the same.
Since, I am passing only 9/14 test cases, there must be some logical error that is leading to a "Wrong Answer". How do I overcome that if I use Code 1? And if I use Code 2, how do I avoid infinite execution of the "Marked Statement"? Or is there anyone who would like to suggest me a Code 3?
Thank you in advance :)

Try saving the new (returned) string instead of ignoring it.
s = s.replace(t,"");
replace returns a new string; you seemed to think that it alters the given string in-place.

Try adding some simple parameter checks of the strings. The strings shouldn't be equal to null and they should have a length greater than 0 to allow for counts greater than 0.
static int maxMoves(String s, String t) {
int count = 0,i;
if(s == null || s.length() == 0 || t == null || t.length() == 0)
return 0;
while(true)
{
if(s.contains(t) && !s.equals(""))
s = s.replace(t,""); //Marked Statement
else break;
++count;
}
return count;
}

You might be missing on the edge cases in the code 1.
In code 2, you are not storing the new string formed after the replace function.
The replace function replaces each substring of this string that matches the literal target sequence with the specified literal replacement sequence.
Try this out:
public static int findCount(String s, String t){
if( null == s || "" == s || null == t || "" == t)
return 0;
int count =0;
while(true){
if(s.contains(t)){
count++;
int i = s.indexOf(t);
s = s.substring(0, i)+s.substring(i+t.length(), s.length());
// s = s.replace(t,"");
}
else
break;
}
return count;
}

String r1="ramraviraravivimravi";
String r2="ravi";
int count=0,i;
while(r1.contains(r2))
{
count++;
i=r1.indexOf(r2);
StringBuilder s1=new StringBuilder(r1);
s1.delete(i,i+r2.length());
System.out.println(s1.toString());
r1=s1.toString();
}
System.out.println(count);

First of all no logical difference in both the codes.
All the mentioned answers are to rectify the error of code 2 but none told how to pass all (14/14) cases.
Here I am mentioning a test case where your code will fail.
s = "abcabcabab";
t = "abcab"
Your answer 1
Expected answer 2
According to your code:
In 1st step, removig t from index 0 of s,
s will reduce to "cabab", so the count will be 1 only.
But actual answer should be 2
I first step, remove t from index 3 of s,
s will reduced to "abcab", count = 1.
In 2nd step removing t from index 0,
s will reduced to "", count = 2.
So answer would be 2.
If anyone know how to handle such cases, please let me know.

Related

Why do I need to use count -=1 in the following code?

When creating the following function, in order to get a correct answer I have to add "count-=1" line, otherwise the answer gets skewed by 1.
public int countCTG(String dna) {
int count = 0;
int firstOccurrence = dna.indexOf("CTG");
if (firstOccurrence != -1) {
count +=1;
while (dna.indexOf("CTG", firstOccurrence) != -1 && firstOccurrence != -1) {
count +=1;
firstOccurrence = dna.indexOf("CTG", firstOccurrence+3);
}
count -=1;
}
else {
count = 0;
}
return count;
}
I managed to get this function working, however could you please help me understand the logic behind it? The count variable was initialized originally to 0 and if a string,for example, contains one instance of "CTG" it will be already counted in by "count +=1" line. Wouldn't count -=1 reset this variable back to 0?
You need the -1 because of the +1 before the loop: the first iteration of the while loop counts the already-found occurrence again.
An easier solution is like so:
int count = 0;
int skip = "CTG".length();
int current = -skip;
while ((current = dna.indexOf("CTG", current + skip)) >= 0) {
++count;
}
return count;
Because you're not updating firstOccurrence after your first search -- i.e. you're searching twice from the start (.indexOf("CTG")) before starting to search from the previous result (.indexOf("CTG", prevResultIndex + 3)).
Also note that:
you don't have to search once before the while loop
the else clause is redundant
you're calling .indexOf twice as many times as you actually need
the firstOccurrence+3 is a liability, you'll forget to update the offset when the string changes and it will be hard to track down. Store the searched-for string in one place, and compute its length instead of hardcoding it.
EDIT: Well #AndyTurner rewrote it for you, but try to see how each one of the listed points come into reaching that result

How do i find how many times a substring is used in a string?

public Object countOccurrences(String string)
{
int e = 0;
int i = 0;
while ( i < sentence.length())
{
if(sentence.contains(string))
{
e++;
i++;
return e;
}
else
{
break;
}
}
return e;
}
What is wrong with my code that won't allow me to pass the test? is there anyway I can use the substring method inside a loop to do this?
#Test
public void testCountOccurrences()
{
assertEquals(1, sc1.countOccurrences("ence"));
assertEquals(2, sc1.countOccurrences("en"));
assertEquals(1, sc2.countOccurrences("that"));
assertEquals(0, sc2.countOccurrences("This"));
}
What is wrong with my code that won't allow me to pass the test?
Before doing anything else, you should consider how you could have worked out what was wrong for your code to start with. Did you debug through it? At what point did it behave differently to how you expected it to? If you haven't learned how to use a debugger yet, now is a great time to start.
As for the next step, look at this code:
if(sentence.contains(string))
{
e++;
i++;
return e;
}
That condition doesn't depend on i or e, just on sentence and string. So as long as sentence is of length at least 1, you'll either return 1 or 0. Your code can never return more than 1.
That's what's wrong with your code at the moment - as for how to fix it, I'd start looking at String.indexOf(String, int). In other words, you want to find the first occurrence, then find the next occurrence, then the next occurrence, until you can't find any more. (Use the return value to work out where to start looking on the next iteration, as well as checking that there was a match.)
A couple of situations to be careful of:
How many times does "abbbc" contain "bb"?
How many times does "abbbc" contain ""?
I'd also urge a couple of other changes:
Your method has a return type of Object - why? Surely it's always going to return an integer, so a return type of int would be more appropriate
This is a great candidate for parameterized testing. Look into how you can effectively separate your single test into multiple test cases which can pass or fail independently, but without the source overhead of lots of test methods... (Hint: each test case should have the sentence, the text you're looking for, and the expected number of matches.)
public Object countOccurrences(String string) {
int e = 0;
int i = 0;
while (i <= (sentence.length() - string.length() + 1)) {
if (sentence.substr(i, string.length() - 1).equals(string)) {
e++;
}
i++;
}
return e;
}
I didn't got to try it myself but it should work.

Working of Return statement in a method in Java

I am trying to understand the working of return statement in JAVA.
My doubt is if inside a method with a Non void return type, I have a decision block which also has a return statement of its own, Still I have to return some value .
For understanding here is a sample code I have written :-
public int bunnyEars(int bunnies) {
//int count=0;
if (bunnies >=1) {
count = count + 2;
bunnyEars(bunnies -1);
return count1;
}
return count2 ;
}
In the mentioned code I just want to return the no. of bunnies which I am being able to do from inside the bunnyEars method count1. But still JAVA wont allow to have a non-void method without a return type which is totally understood and I have to add count2 return also. Now I am suspecting that I am having a conceptual understanding failure here. Kindly let me know if I am missing something? Kindly let me know If I am missing some more info here.
[Edited] Full code:
public class Test5 {
//public int ears=1;
public int count=0;
public int bunnyEars(int bunnies) {
//int count=0;
if (bunnies >=1) {
count = count + 2;
bunnyEars(bunnies -1);
return count;
}
return count ;
}
public static void main(String args[]){
Test5 test5= new Test5();
System.out.println(test5.bunnyEars(90));
}
}
Yes you need to return count2 which should be zero. Which means if there are no bunnies then there are no ears. So which returning you should be returning some value irrespective of the conditional block.
So in this case
return count1;
represents the number of ears if the bunnies are represent, while
return count2;
represents the number of ears when there are no bunnies, which should be 0.
I hope that gives you some clarification
I think your conceptual misunderstanding lies with understanding the flow of the program.
Supposed you were to use this method by calling:
bunnyEars(2)
Then, once you enter the method, the first thing the program does is check if 3 >= 1. Since this is true, you proceed into the code inside the {..} (called a 'block'). Inside this block, you increment count by 2. I am assuming count is defined elsewhere in the class, but suppose the current value for count is 10. Then, the new value of count will be 12.
After this, the program executes the line:
bunnyEars(bunnies - 1)
Which translates to:
bunnyEars(1)
Now, basically, you are calling the same method again, but passing in 1 instead of 2.
Once again, the program checks to see that 1 >= 1, which is true. So it goes into the
if-block which, again, increments count by 2. So now, count = 14. Then it calls the
same method again but this time passing in 0.
bunnyEars(0)
Since 0 >= 1 evaluates to false, you the program skips the if-block and continues
execution after the block. So know, you are in the method bunnyEars(), but you have
completely skipped over your "return" statement. But, alas, bunnyEars MUST return an int.
So this is why you must have a return after the block. In your case, bunnyEars(0) returns count2 and the program-execution returns to where you called bunnyEars(0).
Read up on recursive calls. The basic idea of a recursive method is that, inside the recursive method, you must have some case that terminates the recursion (otherwise you will loop forever).
For example, the following code will go on forever:
public int sum(int in)
{
return in + sum(in - 1);
}
This will keep going on forever, because sum(1) will call sum(0) which calls sum(-1).
So, I must have a condition that terminates the recursion:
public int sum(int in)
{
if(in == 0) return 0;
return in + sum(in - 1);
}
Now, I have a terminating-case. So if I call sum(1), it will call sum(0) which returns 0. So my result is 1 + 0 = 1.
Similarily,
sum(2) = 2 + sum(1) = 2 + 1 + sum(0) = 2 + 1 + 0
sum(3) = 3 + sum(2) = 3 + 2 + sum(1) = 3 + 2 + 1 + sum(0) = 3 + 2 + 1 + 0 = 6
Hope this helps!
So as I understand it, your question is why you still need to return count2 if you return count1. The answer is basically 'what happens if you don't enter the if block?'. In that case, without return count2, you wouldn't have a return value, which is what Java is complaining about. If you really don't want two return statements, you could probably do something like:
public int bunnyEars(int bunnies) {
int count=0;
if (bunnies >=1) {
count = count + 2;
bunnyEars(bunnies -1);
}
return count ;
}
On a side note, this and the code you posted in your question won't work for regression purposes, but the one in your comment does, and there it looks like you have a static variable for count, in which case you could set the return type to void and just print count.

Odd/curious behaviour in a recursive function

I am trying to program a function checking whether or not a string is the result of set of actions on previous string. Specifically, a string 'b' is a transformation of string 'a' if it follows the same order of characters, has only the same characters, yet may have them multiplied. For example:
"aabba" is a transformation of "aba" however not of "abaa", as that has double 'a' at the end.
The code is as follows:
public static boolean isTrans(String s, String t)
{
if(s.equals(t)){return true;} //obviously the latter is transformation
else if(s.length()==0||t.length()==0){return false;} //either is empty, can't be transformation
else if(s.charAt(0)!=t.charAt(0)){return false;} //obviously not transformation
else {return (s.length()==(isTrans(s,0,t,1)));} //recursive method below, note s.charAt(0)==t.charAt(0).
}
private static int isTrans(String s, int i, String t, int j)
{
if(i < s.length() && j < t.length()) //only as long as the indexes are within right bound
{
if(s.charAt(i) == t.charAt(j)) //character at 'transformed' string is the character at original string
{
if((j+1)<t.length()) //only as long as there are more characters
{
j++;
isTrans(s,i,t,j); //check next character at transformed string
}
}
else if((i+1)<s.length()) //ensures there is a next character at string s
{
if(s.charAt(i+1) == t.charAt(j)) //character is the next chracter at original string
{
i++;
if((j+1)<t.length()) //only as long as there are more characters
{
j++;
isTrans(s,i,t,j); //checks next characters at strings
}
}
else{i=-1;} //not transformation
}
else{i=-1;} //not transformation
}
return (i+1);
}
The program doesn't work as intended. Now here is the curious thing: running it through the debugger, it does everything as intended, however, when it reaches the "return (i+1)" command, instead of actually returning it, it starts to execute the recursive calls for some reason, meanwhile decreasing the value of i, until it reaches 0, and only then returning it, causing false negatives. More specifically, it goes up the stack and 'executes' the recursive calls of isTrans(s,i,t,j).
I would like to know why it does that, even more than a way to solve such a problem. It doesn't even enters through the iff's, but immediately enters the recursive calls, reducing the value of i all the way to 0 and only then returning it.
Appreciate any comments!
Edit: to clarify what exactly it does, according to the debugger. If I try to see if "aabba" is a transformation of "aba" (it is under the definitions above), the program reaches the desired value for i - 2. However, it then reaches the command return (i+1), and suddenly returns to line 17 in the given code, then the next recursive call in the code, and back to it - all the meanwhile reducing the value of i back to 0. Only then it executes the command outside of the function.
Edit 2: After a bit of tweaking and playing with the code, it seems that without connection to the return statement, at the end the function 'jumps' backwards, skipping through the 'if's, to the recursive calls - does not execute them, yet reduces i to 0 and only then continues. Does it have something to do with me calling isTrans(s,0,t,1)?
Your exit condition is return i+1. This will never work, even if your logic did, because the result you are returning is always one more than the length of the String.
Here is what likely happens in your code:
First if condition is satisfied
j is incremeanted.
Recursive call.
No more if conditions are satisfied, because i and j no longer point to the same letter.
It returns i+1
To do this recursively you would probably do something like this:
public class Main {
public static void main(String args[])
{
String s = "aba";
String t = "abz";
System.out.println(isTrans(0, 0, s, t));
}
public static boolean isTrans(int si, int ti,String s ,String t)
{
// we have run out of source characters, or the source character has passed the transformation character. This is not a transformation.
if (si > ti || si == s.length())
{
return false;
}
// we are at the end of the transformation string. This might be a transformation.
if(ti == t.length())
{
return si == s.length()-1; // If we're at the end of our source string, otherwise it isn't.
}
// if the current positions contain identical characters...
if (checkChar(si, ti, s, t))
{
// advance the transformation index.
ti++;
}
else
{
// otherwise, advance the source index.
si++;
}
// recursion.
return isTrans(si, ti, s, t);
}
private static boolean checkChar(int si, int ti, String s, String t)
{
return (s.charAt(si) == t.charAt(ti));
}
}
Hope you found it helpful.

Issues with using substring() to find a pattern

Here's what I'm trying to implement:
"If the strand is:
AACATGTACTACTGGTG
and the snip method is called like this:
snip("CA", "A")
then the resulting new DNAStrandJedi object should represent the
strand "TGT" which is found after the first match of "CA" at index 2
and before the subsequent match of "A" at index 7."
And here's my code:
public DNAStrandJedi snip(String startPattern, String endPattern) {
if (!passedStrand.contains(startPattern)
|| !passedStrand.contains(endPattern)) {
return null;
} else {
String snippedString = passedStrand.substring(3, 5);
return new DNAStrandJedi(snippedString);
}
}
The strand that I'm trying to snip is: "AGTCAGTACC"
Here I'm printing the results of the snip() method to the console:
System.out.println("Snip: " + test.snip("GT", "TA").getStrandString());
I should be getting the strand "CAG" but instead I'm just getting CA.
What I'm having trouble with is figuring out the numbers to use as indices to get the snippedString. Would I have to use some sort of for loop here to get the indices of the startPattern and endPattern? Let me know if you find the question confusing and I'll try to explain it better.
The String.indexOf() function is what you need.
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#indexOf(java.lang.String, int)
if (passedStrand.indexOf(startPattern) > -1 && passedString.indexOf(endPattern, passedStrand.indexOf(startPattern)) > -1)
return return new DNAStrandJedi(passedStrand.indexOf(startPattern)+startPattern.length, passedString.indexOf(endPattern, passedStrand.indexOf(startPattern))-1);
else
return null;
(not tested and not optimal, but its something similar)
The code below is written in simple and clear way and it might explain
public DNAStrandJedi snip(String startPattern, String endPattern) {
int sL = startPattern.length();
int startIndex = passedStrand.indexOf(startPattern);
int endIndex = passedStrand.indexOf(endPattern, startIndex + sL);
if ( startIndex == -1 || endIndex == -1 )
return null;
}
String snippedString = passedStrand.substring(startIndex+sL, endIndex);
return new DNAStrandJedi(snippedString);
}
For the information of Java String Functions you can see at here
Actually #vanza appears to have answered your question it's the "substring(3,5)" that is hard coded that is causing the problem.
You reference a "passedStrand" variable, but you don't show how it is initialized...Did you maybe miss something that's related that we need to know to help you solve the problem?
Use indexOf to find the indices...
} else {
int start = passedStrand.indexOf(startPattern) + startPattern.length();
int end = passedStrand.indexOf(endPattern, start);
String snippedString = passedStrand.substring(start,end);
return new DNAStrandJedi(snippedString);
}

Categories

Resources