I am solving a question on LeetCode.com:
A string S of lowercase English letters is given. We want to partition this string into as many parts as possible so that each letter appears in at most one part, and return a list of integers representing the size of these parts.
For the input: "ababcbacadefegdehijhklij" the output is: [9,7,8]
A highly upvoted solution is as below:
public List<Integer> partitionLabels(String S) {
if(S == null || S.length() == 0){
return null;
}
List<Integer> list = new ArrayList<>();
int[] map = new int[26]; // record the last index of the each char
for(int i = 0; i < S.length(); i++){
map[S.charAt(i)-'a'] = i;
}
// record the end index of the current sub string
int last = 0;
int start = 0;
for(int i = 0; i < S.length(); i++){
last = Math.max(last, map[S.charAt(i)-'a']);
if(last == i){
list.add(last - start + 1);
start = last + 1;
}
}
return list;
}
I understand what we are doing in the first for loop (we just store the index of the last occurrence of a character), but I am not too sure about the second one:
a. Why do we calculate the max() and compare last==i?
b. How does it help us achieve what we seek - in the above example, when we encounter a at position 8 (0-indexed), what guarantees that we won't encounter, say b, at a position greater than 8? Because, if we do, then considering 8 as the end position of our substring is incorrect.
Thanks!
If we encounter a character S[i] we may cut the string only after it's last occurence, hence map[S.charAt(i)-'a']. We maximize the value in last because we need to ensure that all the processed characters will have their last occurence in the prefix, so we look at the rightmost of such indices, hence the max. If we encounter a character S[i] such that i is it's last occurrence and all characters before have their last occurrences before i, we may add the substring start..i to the result, and set start = i + 1 for the next substring.
The idea is this. Whenever the last occurrence of a particular character matches the current index, it means that this particular character appears only in this part.
To understand this better, just do this.
int last = 0;
int start = 0;
for(int i = 0; i < S.length(); i++){
last = Math.max(last, map[S.charAt(i)-'a']);
System.out.println(last+" "+i);
if(last == i){
list.add(last - start + 1);
start = last + 1;
}
}
Take your example string "ababcbacadefegdehijhklij".
Now, the output would be
8 0
8 1
8 2
8 3
8 4
8 5
8 6
8 7
8 8
14 9
15 10
15 11
15 12
15 13
15 14
15 15
19 16
22 17
23 18
23 19
23 20
23 21
23 22
23 23
The last occurrence of a is at the 8th position. Now we are at 0th position. Increment i. So, the till we get to 8th position, we cant be sure that each part contains at most 1 character. Suppose the next character is b and it occurs finally in 10th position, then we need to confirm till 10th position.
if(last == i){
}
The above if just confirms that the part is over and we can start a new part from the next index. Before doing this, we are adding the length of the current part to the output.
Related
So I'm having some issues with code I'm writing for an assignment and it's kinda driving me crazy. It's my first semester and I've never done coding before, so I know I still have heaps to learn. Anyways, the issues:
I'm having two problems which could be related but I'm not 100% sure. I'm getting this error:
'Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 125 out of bounds for length 125.'
Which points to the following section of code (Commented on specific line):
public static String gradesDistn() {
String gradeDistn = "0";
//Sort the above letterGrade array
Arrays.sort(letterGrade);
//This loop counts each occurrence of an element in the letterGrade array.
for (int i = 0; i < letterGrade.length - 1; i++) {
int count = 0;
for(int j = i + 1; j < letterGrade.length; j++) {
if (letterGrade[i] == letterGrade[j])
count++;
System.out.println (gradeDistn = letterGrade[i] + ": " + count); //THIS IS THE ERROR LINE.
i += (count-1);
}
}
return gradeDistn;
I can post the full code if additional context is needed. Essentially I'm am trying to count the occurrences of each letter grade (A, B, C, D, E and F) which are stored in an array. Each occurrence is based off of another array which stores number marks and is randomly generated. So the results should look something like this:
A: 6
B: 10
C:20
D: 9
E: 3
F: 1
instead I'm getting (copied from console):
A: 1
A: 2
A: 3
A: 4
B: 5
B: 6
B: 7
B: 8
B: 9
B: 10
B: 11
C: 11
C: 11
D: 11
D: 11
F: 11
C: 20
D: 9
E: 3
F: 1
I've spent so long looking at these code and trying to make it work, I feel like I'm am blind to the potentially obvious issues with it. Any help on this 2 issues would be really appreciated!
Also of note: The assignment requires a bunch of super specific methods such as this so that can't be changed, addition classes can not be used etc.
For this answer, I'm going to put aside the question of ArrayIndexOutOfBoundsException and offer guidance on how to generate a frequency distribution.
You are going to want a set of counters. Each counter will represent a range of values. To start, you should decide what range each counter will represent. Too fine a resolution, and your frequency distribution will not be very useful. For example, if your data is 100 meter sprint times for high school boys, and each counter represents an interval of 1/10,000 of a second, unless your sample size is very large, your frequency distribution would have a lot of values of '1' and '0'. You might want to separate by 1/10 of a second, 1 second, or 2 seconds, depending on how much variation there is in the boys' times.
But, if your data is species of farm animals, it would be fine to have a separate counter for each possible value.
So, determine how many counters you will need. What is the range of possible values? How closely do you want to group them?
Next, you will want a means of linking a particular value to a particular counter. In some cases, a little math can be used. In other cases, an if ... else if ... chain or a switch block is useful.
For the first example, I want to analyze daily high temperatures. I decide to group by 10 degrees Fahrenheit. I could decide on 5 degrees, 7 degrees, or some other interval. It could even be irregular intervals. But, keeping it regular and going by 10 degrees makes the example easier to follow.
Having settled on grouping by 10 degrees, I next want to decide the maximum and minimum. I'll pick -19 as the coldest, and 119 as the hottest. But, I want to allow for occurrences outside of that range. So, to hold the count, I will want an array of 15 counters.
Next, I will want a means of "translating" a temperature measurement to an array index. I use the int variable k for that in the following code. To make it easier to follow, I broke the calculation into 4 lines and then used the result as a subscript:
public static int [] tempFreqDist (WeatherStats [] daily) {
int [] count = new int [15]; // java initializes to zeros
int k;
for (int dIdx = 0; dIdx < daily.length; ++ dIdx) {
k = daily [dIdx].getHighTemp();
k = Math.max (k,-20);
k = Math.min (k,120);
k = k/10 + 2;
count [k]++;
}
return count;
}
That's it! One loop, and no sorting.
We want to group temps of -20 and colder together, regardless of how far below -20. So, we use Math.max. Similarly, we group temps of 120 and hotter by using Math.min. Next, divide by our grouping factor, and adjust the result so the lowest has 0 for the subscript value.
So, the result is the elements of count correspond to the temperature ranges: 0 ➔ -20 and colder; 1 ➔ "teens" (10 to 19) below zero; 2 ➔ single digits below zero; 3 ➔ zero and single digits above; 4 ➔ teens above zero; 5 ➔ twenties, ..., 14 ➔ teens above 100; 14 ➔ 120 and above.
But, suppose the 'width' of categories was irregular? One possibility is that you could use a chain of if ... else:
int t = daily [dIdx].getHighTemp();
if (t <= -20) k = 0;
else if (t <= -13) k = 1;
else if (t <= 0) k = 2;
else if (t <= 15) k = 3;
else if (t <= 28) k = 4;
and so on.
Another example counts animals you might see on a farm.
You can do that with an if ... else if ... chain:
public int[] animalFD (String [] species) {
int [] count = new int [6];
// 0 ➔ cattle, 1 ➔ pig, 2 ➔ sheep,
// 3 ➔ goat, 4 ➔ duck, 5 ➔ horse
...
for (int m = 0; m < species.length; ++m) {
if (species[m].equals("cow") count[0]++;
else if (species[m].equals("pig") count [1]++;
else if ...
But, for something like this, I prefer switch to if ... else chain:
public static int [] animalFD (String [] species) {
int [] count = new int [6];
for (int m = 0; m < species.length; ++m) {
switch (letterGrade [m]) {
case "cow":
count[0]++;
break;
case "pig":
count [1]++;
break;
case "sheep":
count [2]++;
break;
case "goat":
count [3]++;
break;
...
Here is a "trick" you can use to easily convert a letter to an index. Recall that a char is essentially a integer primitive: You can do numeric calculations on it.
char letter;
int idx;
...
if (letter >= 'A' && letter <= 'Z') {
idx = letter - 'A'; // result is zero to 25
...
This takes advantage of the fact that the letters A to Z are consecutive in character set encoding. However, that isn't universal. EBCDIC, for example, has non-letter characters between I and J, and between R and S, IIRC.
Not sure if anyone can explain this to me or help me.
I have a 15 Digit Number of which I want to multiply each even number by 2 unless the even number is greater than 9. If it is this needs to be subtracted by 9 to give me an integer that again I can multiply by 2. Once I have all the even numbers multiplied by 2 i need to add them all together with the odd numbers.
Does that make sense.
UPDATE ***
so i have a number say 49209999856459. for that number I am looking to get the even integer so for example the first even one would be 4 then the second would be 2 and so on.
If one of those even numbers are multiplied by 2 then it might be above 9 so I want to subtract 9 to then use the remainder as the even number in its place.
SO !!!
Multiply by 2 the value of each even digit starting from index 0 and then each even index. In each case, if the resulting value is greater than 9, subtract 9 from it (which reduces larger values to a single digit). Leave the values of the digits at the odd indexes unchanged.
public String calculateCheckNumber()
String firstFifteen = longNumber.substring(0,15) ;
int i, checkSum = 0, totalSum = 0;
for (i = 0; i<firstFifteen.length(); i += 2) {
while (i < 9)
i *= 2;
if (i > 9)
i -= 9 ;
}
Was one option I was trying but it honestly I cant seem to get my head around it.
Any Help would be greatly appreciated.
Well, here is one approach. This uses the ternary (?:) operator to condense the operations. Edited base on clarification from the OP. The example you gave is actually a 14 digit string. But the following will work with any number of digits if they start out in a string. If you have a long value, then you can create the character array using:
long v = 49209999856459L;
char[] d = Long.toString(v).toCharArray();
Here is the main algorithm.
String s = "49209999856459";
int sum = 0;
char[] d = s.toCharArray();
for (int i = 0; i < d.length; i++) {
int v = d[i] - '0';
// The even digit will only be greater than 9 after
// doubling if it is >= 5 before.
sum += ((i % 2) == 1) ? v : (v >= 5) ? v+v-9 : v+v;
}
System.out.println(sum);
Prints
86
I'm trying to prepare data to be used inside of a histogram. I want 2 arrays, one is all the temperatures from my highest and lowest collected temperature. The second array will contain the frequency of each temperature.
int difference is the difference from the lowest and highest temp
Array temp contains the temperatures collected
HashMap map contains the frequencies collected of each temp
Array tempGaps contains the temperatures + every other temp that was not collected
Array finalTemps contains the frequency of each temp.
The goal I hope to achieve is two arrays, one with all the temperatures, one with the frequencies of each and their index value is what corresponds them to eachother.
public void fillGaps() {
int j = 0;
tempGaps = new int[difference];
finalTemps = new int[difference];
for (int i = 0; i < difference; i++) {
tempGaps[i] = temp[0] + i;
if (tempGaps[i] == temp[j]) {
finalTemps[i] = map.get(new Integer(tempGaps[i]));
j++;
} else {
finalTemps[i] = 0;
}
}
}output: https://pastebin.com/nFCZXFyp
Output:
7 ->1 9 ->1 10 ->1 12 ->1 14 ->2 15 ->1 16 ->1 18 ->2 19 ->1 21 ->1
TEMP GAPS
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
FINAL TEMPS
1 0 1 1 0 1 0 2 0 0 0 0 0 0 0
My finalTemp output stops after 14 degrees - occurs 2 times. It does this after any data set I put in which has a frequency of more than 1. Please help! Thanks!!
I don't believe you need the j variable. It seems to make things confusing, especially because the for loop is only a single pass-through. Instead, fetch the value from the map. If null, assign 0, otherwise, assign the value.
public void fillGaps() {
tempGaps = new int[difference];
finalTemps = new int[difference];
for (int i = 0; i < difference; i++) { //navigate through every temperature
tempGaps[i] = temp[0] + i; //assign the current temperature
Integer frequency = map.get(new Integer(tempGaps[i])); //fetch the frequency
finalTemps[i] = frequency == null ? 0 : frequency; //assign 0 if null, otherwise frequency if not null
}
}
I am doing some kind of cipher in Java for schools homework. The task is to change the value of a certian char to a new one with a specific offset which is given by the user and has a range from negative numbers to positive numbers (alphabet).
Now I have a problem with negative offsets. I have created a String with the Alphabet which helps to find the new char. For example: With the offset of 7 I got this: encrypt(“TEST”) = “ALZA”. So my code grabs the index of the string value and searches with this index in the alphabet string for the new char. Anyway when I now have the char 'E' and a negative index i.e '-7' it will return the value of -3 for the new index of the new char (I hope that makes sense). Since there is no char on index '-3' I get an error.
So how can I access to the end of the string instead of going more and more into negative index numbers ?
Add 26 then mod 26:
i = (i + 26) % 26;
This always works for indexes down to -26. If that's not enough, just add some zeroes:
i = (i + 26000000) % 26;
Your general problem appears to be that letters are represented by only 26 indices, but the actual index variable you use might be greater than 26, or even less than zero (negative). One way to handle this problem is to use the mod operator to safely wrap your index around to always point to a range containing a valid letter.
Here is logic which can do that:
if (index < 0) {
index = (index % 26) + 26;
}
else {
index = index % 26;
}
Assuming the letter E is position 5 and you have a reassignment of -7, this would mean that the new index would be -2. This new position can be mapped using the above logic as follows, where index = -2 in this case:
5 - 7 = -2
(-2 % 26) + 26
-2 + 26
24
And character in position 24 is the letter X.
If you can constrain shift values to be positive, you can use remainder operator:
int newIndex = (index + shift) % 26
If there are negatives to be expected:
int newIndex = Math.floorMod(inndex + shift, 26) would do the trick
Actually you need mathematical modulo, but % operator is not quite that
I have created an int array to hold 100 values and have initialized them to be random integer values from 1-100.
Now my task is to output this array in a 10x10 block separated by tabs.
for example:
48 49 88 ..... (until 10 numbers appear on this line)
45 1 55 ..... (until 10 numbers appear on this line)
59 21 11 ..... (until 10 numbers appear on this line)
until 10 numbers appear going down as well
I'm kind of lost on how to do this, any tips?
A for loop is the easiest way to do this. Assuming x is the name of your single-dimensional, 100-element int array:
for(int i = 0; i < x.length; ++i)
{
System.out.print(x[i]);
if((i % 10) == 9)
{
System.out.print("\n");
//or System.out.println();
//both print a new line
}
else
{
System.out.print("\t");
}
}
\t gives you a tab, \n gives you a new line.
The (i % 10) == 9 part checks to see if you are on the 10th column. If you are, then you create a new line with the \n character.
This will iterate to the end of the array as well and no further, so if you ended up having a different number of columns than 100, this would not run the risk of trying to access an element that does not exist.
What you have to do, is to put a a newline-sign "\n" after each 10th element and a tab "\t" after each other one
Let's say a is your array. Then you could accomplish this at example by doing:
for(int i = 1; i <= a.length; i++){
//print out the element at the i-th position
System.out.print(a[i-1])
//check, if
if(i % 10 == 0){
//you could also simply use System.out.println()
System.out.print("\n");
}else{
System.out.print("\t");
}
}
If you just want to print them in that maner. Use som form of while loop that prints out 10 elements with a tab between them and last element Will have a new line sign because of a If statement for 10th element. This van be done through some form of counter.