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

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

Related

Cut out different elements from a string and put them into a list

Here's updated code. For those following along the question edits contains the original question.
if (0 != searchString.length()) {
for (int index = input.indexOf(searchString, 0);
index != -1;
index = input.indexOf(searchString, eagerMatching ? index + 1 : index + searchString.length())) {
occurences++;
System.out.println(occurences);
indexIN=input.indexOf(ListStringIN, occurences - 1) + ListStringIN.length();
System.out.println(indexIN);
System.out.println(ListStringIN.length());
indexOUT=input.indexOf(ListStringOUT, occurences - 1);
System.out.println(indexOUT);
Lresult.add(input.substring(indexIN, indexOUT));
System.out.println();
}
}
As you can see, I gave me out the index numbers
My code works well with only one Element
But when I write something like this: %%%%ONE++++ %%%%TWO++++
There's this exception:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 16, end 7, length 23
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3410)
at java.base/java.lang.String.substring(String.java:1883)
at com.DMMS.Main.identify(Main.java:81)
And I found out that the indexIN changes in the Start of the second String but not the indexOUT
I couldn't find out why
When you look at your code you can notice: in the first loop that counts the number of occurrences, your code "knows" that it has to use that version of indexOf() that relies on offsets within the search strings.
In other words: you know that you have to search after previous "hits" when walking through your string.
But your second loop, the one that has to extract the actual things, there you are using indexOf() without that extra offset parameter. Therefore you keep "copying out" the same part repeatedly.
Thus: "simply" apply the same logic from loop 1 for loop 2!
Beyond that:
you don't need two loops for that. Counting occurrences and "copying out" the matching code ... can be done in one loop
and honestly: rewrite that first loop. This code is almost incomprehensible for human beings. A reader would have to sit down and read this 10, 20 times, and then run it in a debugger to understand what it is doing
I dit it!
Heres the code:
.........................
static String ListStringIN = "%%%%";
static String ListStringOUT = "++++";
........................
else if (input.contains(ListStringIN) && input.contains(ListStringOUT)) {
System.out.println("Identifiziere Liste...");
String searchString = ListStringIN;
int occurences = 0;
boolean eagerMatching = false;
if (0 != searchString.length()) {
for (int index = input.indexOf(searchString, 0); index != -1; index = input
.indexOf(searchString, eagerMatching ? index + 1 : index + searchString.length())) {
occurences++;
System.out.println(occurences);
indexIN=input.indexOf(ListStringIN, occurences - 1) + ListStringIN.length();
System.out.println(indexIN);
//indexOUT=input.indexOf(ListStringOUT, occurences);
//indexOUT=input.indexOf(ListStringOUT, occurences - 1);
indexOUT = input.indexOf(ListStringOUT, eagerMatching ? index + 1 : index + ListStringOUT.length());
System.out.println(indexOUT);
Lresult.add(input.substring(indexIN, indexOUT));
System.out.println();
}
}
//for (int i = 0; i <occurences; i ++) {
// Lresult.add(input.substring(input.indexOf(ListStringIN, 0) + ListStringIN.length(), input.indexOf(ListStringOUT)));
//}
result = Lresult.toString();
return result;
}
I hope this is useful for other people
#GhostCat Thanks for your advices!

Removing a substring from a string, repeatedly

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.

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.

Line Not Found on while loop?

I've been trying to get this to work for a while. I'm putting in three parallel empty arrays and it errors out saying that there is no line found. It ONLY works when I change the while statement to the number of elements. I am trying to make arrays that are the size of 15, but only fill the first ten array portions.
Sorry if it sounds complicated, but basically I'm trying to say that the size of the array is 15, I only have 10 things to enter in the array, and the rest of them should be blank.
while (text.hasNext() && c < nameArray.length) {
nameArray[count] = text.nextLine();
intArray[count] = text.nextDouble();
doubleArray[count] = text.nextInt();
text.nextLine();
c++;
}
This does not work.
while (text.hasNext() && c < 9) {
nameArray[count] = text.nextLine();
intArray[count] = text.nextDouble();
doubleArray[count] = text.nextInt();
text.nextLine();
c++;
}
This does.
Your read the file TWICE within a single loop. Remove the 2nd read:
fileText.nextLine();
Of course it doesn't work. If you need to cycle to the max between fileText length and gameArray length, you should use or instead of and and use an if in the loop.
Try something like this:
while (fileText.hasNext() || count < gameArray.length) {
if (!fileText.hasNext()) {
gameArray[count] = "";
priceArray[count] = 0;
stockArray[count] = 0;
} else {
gameArray[count] = fileText.nextLine();
priceArray[count] = fileText.nextDouble();
stockArray[count] = fileText.nextInt();
fileText.nextLine();
}
count++;
}
Your issue is not with the length of the array but with fileTest.nextLine(). After a certain point there is no nextLine() available. It works for the 1st 9 times but I guess all the lines are exhausted before you reach array.length. I would suggest just one condition in your while loop:
while(fileText.hasNext()) {
}
This way you would fill in only the amount actually present.
Add another check with the second fileText.nextLine() to ensure that there is a line to read.
while (fileText.hasNext() && count < gameArray.length) {
gameArray[count] = fileText.nextLine();
priceArray[count] = fileText.nextDouble();
stockArray[count] = fileText.nextInt();
if ( fileText.hasNext() )
fileText.nextLine();
count++;
}
In the first version of your code, your try to read input 15 times, but it is entered only 10 times. So the scanner tries to read a new line but it doesn't exist.

Changing recursive method to iterative

i have recrusive function which works fine. The problem is it gives stackoverflow error when the number of lines are huge. I want to put it in iterative, probably using a for loop. Need some help in doing it.
private TreeSet validate(int curLine, TreeSet errorSet) {
int increment = 0;
int nextLine = 0;
if (curLine == lines.length || errorSet.size() != 0) {
return errorSet;
} else {
String line = lines[curLine];
//validation starts. After validation, line is incremented as per the requirements
increment = 1 //As per requirement. Depends on validation results of the line
if (increment > 0) {
try{
Thread.currentThread().sleep(100);
}catch(Exception ex){
System.out.println(ex);
}
nextLine = (curLine + increment);
validate(nextLine, errorSet);
}
}
return errorSet;
}
Poster's description of the method:
The method does validates textlines, these lines has instructions of how much line has to be skipped, if the line is valid. So, if the line is valid that many of lines will be skipped using the increment. if the line is not valid increment will be 0.
I'm not sure why this was ever recursive in the first place. This is perfectly suited for the use of a FOR loop. use something like so:
private TreeSet validate(int curLine, TreeSet errorSet) {
int increment = 0;
if (errorSet.size() != 0)
return errorSet;
for (int curLine = 0; curLine < lines.Length; curLine += increment)
{
// put your processing logic in here
// set the proper increment here.
}
}
If the increment is always going to be 1, then you can just use curr++ instead of curLine += increment
for(String line : lines) {
// validate line here
if(!errorSet.isEmpty()) {
break;
}
}
The solution for your problem could be simple for loop or while, with logical expression for stop condition. Typically we use for loop when we have to pass through all elements of Iterable or array. In case when we are not aware how many loops we are going to do we use a while loop. Advantage of for loop over while, is that we for free have localized variables so we ca not use them out side of the loop, therefore we reduce possibility to have some bug.
You problem is that you have to break the program on two conditions:
When errorSet is not empty.
When the array of lines have no longer items.
As contradiction, we can say that your program should continue:
Until errorSet is empty,
and until line number is smaller than array size where they are stored.
This provide us to simply expression
errorSet.isEmpty()
lineNumber < lines.length()
We can combine them using logical operator && and use as a stop rule in for loop.
for(int lineNumber= 0; errorSet.isEmpty() && lineNumber< lines.length(); lineNumber++) {
//code to operate
}
Note:
Typically for logical expression is used operator &&, that assure that every part of the logical expression is evaluated. An alternative for this is &, that in case of false do not operate longer and return false. We could be tempted to use this operator for this expression but i would be bad idea. Because when we would traversed all lines without error code will generate IndexOutOfBoundException, if we switch the places then we would not have any optimization as first expression would be evaluated same number of times.

Categories

Resources