Need help understanding parts of this class - java

I need someone to elaborate on certain parts of this code.
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() -
substring.length();
test:
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
more specifically, I don't understand this part:
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
Why is it necessary to declare j and k in this code at all? I know that there is a reason in it for the if statement
if (searchMe.charAt(j++) != substring.charAt(k++))
but I don't understand what the code is actually doing at this part.
Also, what does
while (n-- != 0)
mean?

while (n-- != 0)
This is just looping around, reducing n by 1 each time around the loop and ending when n (before) reducing it by 1 is not 0.
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
This code is starting with j and k at different positions in the string, and then it is looping through comparing the character in the String at that position. j++ just says "use the current value of j, and then afterwards add 1 to it".

++ and -- are pre or post incrementor and decrementor in java.
Imagine the following code to understand the behaviour:
int a = 42
System.out.println(a++) // prints 42
System.out.println(a) // prints 43
System.out.println(++a) // prints 44
System.out.println(a) // prints 44
In fact, it adds or subtracts 1 before or after the statement is processed.
So in your situations, while (n-- != 0) means, that the condition is checked, wether n is not zero and after that decremented by 1.
To achieve the same, you could also write:
while (n != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
n = n - 1 // or n-- or n -= 1
}
Your second condition if (searchMe.charAt(j++) != substring.charAt(k++)) compares the character at the index j in searchMe against the character with the index k from substring and after that increments both indices to avoid two more lines, where these two are incremented.

Well, this is some interesting code. To start off, the label on your break is unnecessary.
Next, to your main questions:
n-- is a postfix decrement operator - the current value of n is evaluated, then it is decremented by 1. When n is evaluated next, it will have the value of n-1.
In the context of the code while(n-- != 0) implies that, when n == 1, this loop will still execute, but the next time we see n, we will be viewing 0 instead.
The statement:
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
...indicates that, if the value in the position of the main search string doesn't match up with the value we're looking for, we need to immediately jump to the label test. This allows you to skip the execution of the following two statements after it, and continue looping and looking for positive matches.
j is constantly set to what i is, but it is not always equal to i. If a partial positive match is found, then j will increment faster than i by virtue of the while loop around that statement.

This can be broken down in to the following:
outer: loop(/* for each character in searchMe to max */) {
loop(/* for each character in substring
* starting with current index in outer loop */) {
if(/* this substring does not match */)
continue /* with next outer loop iteration */;
}
/* if inner loop completed
* the substring did match at this index so break */;
}
Out of all the variables, n is actually the one that's not needed. I don't know why it's there except to try to confound. The while loop could just as easily read while(k < substring.length()).
This kind of loop is necessary because to search for a substring you have to search starting at every character:
Loo
ook
ok
k f
fo
for
or
r a
...
a s
su
sub <- found it
Another way to express the same logic is the following but this creates a new object on each iteration:
int max = searchMe.length() - substring.length();
for(int i = 0; i <= max; i++) {
String toTest = searchMe.substring(i, i + substring.length());
if(toTest.equals(substring)) {
foundIt = true;
break;
}
}

Starting with an n >= 0 (string length can not be negative):
while (n-- != 0) {
...
}
is just a substitute for
for (int t = n ; t > 0; t--) {
.... // use t instead of n, no change in your example
}
it iterates just n times (length of substring).

class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() -
substring.length(); //the length of what we're going to search through, we
//subtract the substring.Length because if the 3rd to
//last character of searchMe is not equal to the first
//character of substring then searchMe can not contain substring.
test:
for (int i = 0; i <= max; i++) { //repeat until we pass the 3rd to last character of searchMe
int n = substring.length(); //resets n to the length of the substring
int j = i; //resets j to equal i so we search for the next letter in searchMe
int k = 0; //resets k to equal 0 so we search for the first letter in substring
while (n-- != 0) { //repeat this loop until n = 0, each pass will subtract 1 from
//n. this will loop a maximum of 3 times (the number of characters in substring)
if (searchMe.charAt(j++) != substring.charAt(k++)) {
//the if statement above will add 1 to j and add 1 to k after it checks to make sure the
//characters are not equal to each other. if they are equal then it will take the new
//j and k variables and repeat the while loop.
continue test; //if statement true then loop through the 'test' again.
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}

Oracle doesn't know how to write simple things in a tutorial for beginners.
This part:
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
can be changed to:
int n = substring.length();
for (int k = 0; k < n; k++) {
if (searchMe.charAt(i + k) != substring.charAt(k)) {
continue test;
}
}
So, you don't need j and instead of that tricky while you can use a simple for which is more intuitive, because it iterates through the substring.

Related

How to obtain the length of the last word in the string

I have reversed the string and have a for loop to iterate through the reversed string.
I am counting characters and I know I have a logic flaw, but I cannot pinpoint why I am having this issue.
The solution needs to return the length of the last word in the string.
My first thought was to iterate through the string backward (I don't know why I decided to create a new string, I should have just iterated through it by decrementing my for loop from the end of the string).
But the logic should be the same from that point for my second for loop.
My logic is basically to try to count characters that aren't whitespace in the last word, and then when the count variable has a value, as well as the next whitespace after the count has counted the characters of the last word.
class Solution {
public int lengthOfLastWord(String s) {
int count = 0;
int countWhite = 0;
char ch;
String reversed = "";
for(int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
reversed += ch;
}
for(int i = 0; i < reversed.length(); i++) {
if(!Character.isWhitespace(reversed.charAt(i))) {
count++;
if(count > 1 && Character.isWhitespace(reversed.charAt(i)) == true) {
break;
}
}
}
return count;
}
}
Maybe try this,
public int lengthOfLastWord(String s) {
String [] arr = s.trim().split(" ");
return arr[arr.length-1].length();
}
Another option would be to use index of last space and calculate length from it:
public int lengthOfLastWord(String string) {
int whiteSpaceIndex = string.lastIndexOf(" ");
if (whiteSpaceIndex == -1) {
return string.length();
}
int lastIndex = string.length() - 1;
return lastIndex - whiteSpaceIndex;
}
String.lastIndexOf() finds the start index of the last occurence of the specified string. -1 means the string was not found, in which case we have a single word and length of the entire string is what we need. Otherwise means we have index of the last space and we can calculate last word length using lastIndexInWord - lastSpaceIndex.
There are lots of ways to achieve that. The most efficient approach is to determine the index of the last white space followed by a letter.
It could be done by iterating over indexes of the given string (reminder: String maintains an array of bytes internally) or simply by invoking method lastIndexOf().
Keeping in mind that the length of a string that could be encountered at runtime is limited to Integer.MAX_VALUE, it'll not be a performance-wise solution to allocate in memory an array, produced as a result of splitting of this lengthy string, when only the length of a single element is required.
The code below demonstrates how to address this problem with Stream IPA and a usual for loop.
The logic of the stream:
Create an IntStream that iterates over the indexes of the given string, starting from the last.
Discard all non-alphabetic symbols at the end of the string with dropWhile().
Then retain all letters until the first non-alphabetic symbol encountered by using takeWhile().
Get the count of element in the stream.
Stream-based solution:
public static int getLastWordLength(String source) {
return (int) IntStream.iterate(source.length() - 1, i -> i >= 0, i -> --i)
.map(source::charAt)
.dropWhile(ch -> !Character.isLetter(ch))
.takeWhile(Character::isLetter)
.count();
}
If your choice is a loop there's no need to reverse the string. You can start iteration from the last index, determine the values of the end and start and return the difference.
Just in case, if you need to reverse a string that is the most simple and efficient way:
new StringBuilder(source).reverse().toString();
Iterative solution:
public static int getLastWordLength(String source) {
int end = -1; // initialized with illegal index
int start = 0;
for (int i = source.length() - 1; i >= 0; i--) {
if (Character.isLetter(source.charAt(i)) && end == -1) {
end = i;
}
if (Character.isWhitespace(source.charAt(i)) && end != -1) {
start = i;
break;
}
}
return end == -1 ? 0 : end - start;
}
main()
public static void main(String[] args) {
System.out.println(getLastWord("Humpty Dumpty sat on a wall % _ (&)"));
}
output
4 - last word is "wall"
Firstly, as you have mentioned, your reverse string formed is just a copy of your original string. To rectify that,
for (int i = s.length() - 1; i >= 0; i--) {
ch = s.charAt(i);
reversed += ch;
}
Secondly, the second if condition is inside your first if condition. That is why, it will never break ( because you are first checking if character is whitespace, if it is, then you are not going inside the if statement, thus your second condition of your inner if loop will never be satisfied).
public class HW5 {
public static void main(String[] args) {
String s = "My name is Mathew";
int count = lengthOfLastWord(s);
System.out.println(count);
}
public static int lengthOfLastWord(String s) {
int count = 0;
int countWhite = 0;
char ch;
String reversed = "";
System.out.println("original string is----" + s);
for (int i = s.length() - 1; i >= 0; i--) {
ch = s.charAt(i);
reversed += ch;
}
System.out.println("reversed string is----" + reversed);
for (int i = 0; i < reversed.length(); i++) {
if (!Character.isWhitespace(reversed.charAt(i)))
count++;
if (count > 1 && Character.isWhitespace(reversed.charAt(i)) == true) {
break;
}
}
return count;
}
}
=
and the output is :
original string is----My name is Mathew
reversed string is----wehtaM si eman yM
6
Another way to go about is : you use the inbuilt function split which returns an array of string and then return the count of last string in the array.

Using return vs While(condition) methods To Break Loops

I'm doing a little google interview question. Find the pair of numbers in a loop that add up to the number given. I found the numbers 2 and 6 that make up 8 so I say match = true so that the while loop stops, however it still proceeds until it finds the second which is 6 and 2 however, those numbers I have already found just the other way around and I had expected my loop to break as my if statement states if there is any 2 numbers that give the sum, match = true therefore terminating the loop, I guess I am wrong though.
However, if I get rid of the while statement and just return; once a match is found it breaks without looking for the second match (which I want it to).
Why is this happening, the logic of both seems the exact same to me.
Using the while(condition) Method
public class Main {
public static void main(String[]args){
int[] list = new int[]{1,2,1,1,1,6};
boolean match = false;
int sumNeeded = 8;
while(!match){
for(int i = 0; i < list.length; i ++){
for(int j = (list.length -1); j >= 0; j --){
if(list[i] != list[j]){
if(list[i] + list[j] == sumNeeded){
System.out.println("The numbers are = " + list[i] + " & " + list[j]);
match = true;
}
}
}
}
}
}
}
Using return
public class Main {
public static void main(String[]args){
int[] list = new int[]{1,2,1,1,1,6};
int sumNeeded = 8;
for(int i = 0; i < list.length; i ++){
for(int j = (list.length -1); j >= 0; j --){
if(list[i] != list[j]){
if(list[i] + list[j] == sumNeeded){
System.out.println("The numbers are = " + list[i] + " & " + list[j]);
return;
}
}
}
}
}
}
In your while-loop implementation, if the array doesn't have the desired pair at all it would result in an infinite loop. There is no need for the while statement in your solution.
After you enter into the while loop, you look for all the possible pairs in the array and then check for their sum. If it equals the desired sum, you make the boolean variable match as true.
But, until the nested for loop is completely executed (i.e., all the possible pairs are checked) we do not check for the while condition. The entire nested for loop is executed in one iteration of the while loop. Then, the while loop condition is checked again.
As by the end of the first iteration of the while loop all the possible pairs are accounted for, there is no need for a while loop.
Moreover, there are other logical errors in your implementation. The correct brute-force implementation is as follows:
public class Main {
public static void main(String[]args){
int[] list = new int[]{1,2,1,1,1,6};
boolean match = false;
int sumNeeded = 8;
for(int i = 0; i < list.length; i ++){
for(int j = (list.length -1); j > i; j --){
if(list[i] + list[j] == sumNeeded){
System.out.println("The numbers are = " + list[i] + " & " + list[j]);
return;
}
}
}
}
}
The inner-for loop is modified to reduce the double-counting of the unordered pairs. Whenever a match is found and printed, we exit the function.
You may also add a break statement inside the while loop in your initial implementation.
if(match == true) {
break;
}
The while condition continues to execute the first and second for-loop until it's finished where as with return it stops execution entirely from the first and second loop.
To fix the while loop you could use a label and then break from that.
firstLoop:
for(int i = 0; i < list.length; i ++) {
match = true;
break firstLoop;

Recursion java - differently for loop does not understand how to work

I understand the principle of recursion and code but do not understand the loop for,Why variables are in the same line, we can explain how the loop works?
this line:
for (int i = digit, j = 1; i >= 1; i--, j++)
The code:
public static boolean hasSubSeries(int[] arr, int digit) {
return hasSubSeriesHelper(arr, arr.length, digit);
}
public static boolean hasSubSeriesHelper(int[] arr, int size, int digit) {
if (size == 0)
return false;
for (int i = digit, j = 1; i >= 1; i--, j++) {
if (arr[size - j] != i)
return hasSubSeriesHelper(arr, size - 1, digit);
}
return true;
}
thank's
The structure of the for loop is as follows:
for( starting conditions; verification at each loop; action at end of loop)
In your specific case:
for (int i = digit, j = 1; i >= 1; i--, j++)
Starting conditions:
A variable i which is equal to the value contained in the variable digits that is given at the start of the function hasSubSeriesHelper(int[] arr, int size, int digit).
A variable j which starts at 1.
Verification at each loop:
Once each loop is completed we will check this, if it is True, we keep on looping, if not we exit the loop.
We check to see if i declared at the start is greater than or equal to 1, if it is we keep on looping, if not we stop.
Action at end of loop:
We do two actions i-- which decreases the value of i by 1 and j++ which increases the value of j by 1.
To summarise you could translate it to a while loop if you prefer:
int i = digit;
int j = 1;
while ( i >= 1 ) {
// the code inside the for loop goes here
i--;
j++;
}
Note that the actions at the end happen inside the loop whilst the starting condditions go before the loop.
That should also clarify why you can have various declarations in the same line as long as they are of the same type.
You have three parts in a for loop
N°1 : Initialization of variable(s)
N°2 : Boolean expression evaluated to continue/stop looping
N°3 : Changes operated on variables
1
You may have multiple initialization in the first part, as long as the type stay the same, the following is entirely possible :
We declare and instantiate three variables within the first part of our for declaration.
for(int i = 2, j = 2*i, k = 4*j ; i < 3 ; i++) {
System.out.println(i + " " + j + " " + k); // 2 4 16
}

How to find the number of zero elements between sequential non-zero elements

I want to know how many zeros are between sequential non-zero elements in f array. This is how I do it:
int[] f = new int[]{0,0,3,0,0,1};
int start = 0, end = 0;
for (int i=0; i<f.length; i++)
{
if (f[i] != 0 && start == 0)
start=i;
else if (f[i] != 0 && start != 0)
end=i;
}
int cnt = (end-start-1)>=0 ? (end-start-1) : (start-end-1);
The answer is 2 zeros between 3 and 1.
However, in case of int[] f = new int[]{0,3,2,0,0,1} the answer is 3, which is not correct. It should be 2.
Update:
I want to count the number of zeros between LAST left-hand side non-zero element and FIRST right-hand side non-zero element.
Your logic of detecting when there are changes (from 0 to non-0 and from non-0 to 0 elements) is flawed.
You should look at the i and i-1 elements instead. Consider the following:
The start index should be when element i-1 is non-zero and element i is 0.
The end index should be when element i-1 is 0 and element i is non-0 and a start element was found (start > 0, this is to take into account the fact that the array can start with 0 and there were no start sequence).
The next thing to consider is that there may be multiple cases of 0's enclosed in non-0's in the array so each time we encounter an end element, we need to add this to a current count.
Putting this into code:
private static int countZeros(int[] f) {
int start = 0, end = 0, cnt = 0;
for (int i = 1; i < f.length; i++) {
if (f[i-1] != 0 && f[i] == 0) {
start = i;
} else if (f[i-1] == 0 && f[i] != 0 && start > 0) {
end = i;
cnt += end - start;
}
}
return cnt;
}
Some examples:
System.out.println(countZeros(new int[]{0,0,3,0,0,1})); // prints 2
System.out.println(countZeros(new int[]{3,0,0,1})); // prints 2
System.out.println(countZeros(new int[]{3,0,0,1,0})); // prints 2
System.out.println(countZeros(new int[]{3,0,0,1,0,1})); // prints 3
If you want the last 0s group, count backwards.
private static int countZeros(int[] f) {
for (int i=f.length-1; i>=0; i--){
if(f[i]!=0 && i>0 && f[i-1]==0){
i--;
int count=0;
while(i>=0 && f[i]==0){
i--;
count++;
}
if(i==0)
return null;
else
return count;
}
}
return null;
}
Your loops behaviour will have the following effect:
for (int i=0; i<f.length; i++)
{
if (f[i] != 0 && start == 0) //finds only first non-zero in whole array
start=i;
else if (f[i] != 0 && start != 0) //finds last non-zero in whole array
end=i;
}
But by taking the difference you include all non-zeros and zeros in the range. So you need a way to count only zeros between these to points, which is hard to add to the first loop because end will be changing. The easiest solution is to loop a second time from start to end and count the zeros:
int cnt = 0;
for(int i=start; i<end; i++)
if(f[i]==0)
cnt++;
This may not the most efficient solution, but it is a way to build upon your existing logic.

What is wrong with this method using an int[][]?

This method is supposed to return true if there is more than one 1 in a column of a 2D array, yet it doesn't work. I can't figure out what's wrong with it so I thought I'd get some expert opinions.
Example:
10010
01001
10100
will return true because there are 2 ones in the first column.
Here is the code
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
boolean break2 = false;
boolean check = false; //false means no 2 (or more) queens in same column
for (int i = 0; i < x.length; i++) {
count = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
count++;
}
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
}
return check;
}
You break at the first occurance of 1 in whole array, which is probably not the expected result.
Explanation of how your code works:
loop until counter i is less than length of array (number of rows in array)
loop until counter j is less than length of i-th row (number of columns or elements in array)
check if element on i-th row and j-th column is 1, if true, increase variable count by one
if count is greater than 1 (this means it has to be 2 or greater) set break2 and check to true, break
if break2 is true (which is as count is > 2 and first loop breaks), break this loop too:
this happens in 1st row of your example table
end of loops, return check (which is true because 1st row contains 2 ones)
The problem in your code is that you break when you find your first row that satisfies your condition, but you do not (necessarily) check all the rows in given array.
I have corrected the code for you, hopefully it works (untested)
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
for (int i = 0; i < x.length; i++) {
int rowCount = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
rowCount++;
}
if(rowCount > 1) {
count++;
break;
}
}
}
// returns true if count of lines containing 1 equals length of array,
// if not, returns false
return count == x.length;
}
Start of by improving your naming convention. Your code has many variables named by their contents, instead of named by how they are used. For example:
boolean check = false; // false means no two queens in the same column.
instead of
boolean twoQueensInColumn = false;
and the other example
boolean break2 = false;
instead of the more reasonable
boolean continueLooking = true;
Plus, it is a very good idea to avoid using variables as place holders for loop escaping logic. For example, the two stanzas
...
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
are a breeding ground for bugs, requiring a lot of debugging to ensure they work "right now" which will break just as soon as you modify the code. Much better would be
boolean keepLooking = false;
for (int row = 0; keepLooking && (row < board.length); row++) {
int queensInColumn = 0;
for (int column = 0; keepLooking && (column < board[row].length, column++) {
if (board[row][column] != 0) {
queensInColumn++;
}
if (queensInColumn > 1) {
keepLooking = false;
}
}
}
The main difference being the control logic is in the loop "conditional" block, where it belongs.
I would recommend turning your integers to string and using the .contains() method and looping through that. This would make the code easier to understand.

Categories