String Radix Sort - StringIndexOutOfBoundsEception - java

I am writing my own Radix Sort method to sort the words in a String (the big black cat sat on the 
beautiful brown mat would be sorted as beautiful big black brown cat mat on sat the the). The method takes in a List (my own List interface) of the individual words and reorders the list in place.
Here is my method so far:
public static void stringRadixSort(List<String> list, int letters) {
List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 26);
int letterNumber = 1; //Sorts list by 1st letter of each word, then 2nd etc.
for (int i = 0; i < letters; i++) {
while (!list.isEmpty()) {
String word = list.remove(list.first());
if (word.length() > letters) throw new UnsortableException("The list contains a word that holds more letters than the given maximum number of letters."
+ "\nMax Letters: " + letters + "\nWord: " + word);
String letter = word.substring(letterNumber - 1, letterNumber); //EXCEPTION THROWN
char ch = letter.charAt(0);
int index = ch - 'a'; //gets index of each letter ('a' = buckets[0], 'z' = buckets[25]
if (buckets[index] == null) {
buckets[index] = new LinkedList<String>();
}
buckets[index].insertLast(word);
}
for (int j = 0; j < buckets.length; j++) {
if (buckets[j] != null) {
while (!buckets[j].isEmpty()) {
list.insertLast(buckets[j].remove(buckets[j].first()));
}
}
}
letterNumber++;
}
}
The (only, I hope) problem with my method is that when I am reading each character of the word, I create a single letter substring of the word. As the outer for loop runs through letters times (where letters is the maximum length of a word in the List), the exception is thrown when this loop is on an iteration greater than the length of the current word - i.e. letterNumber > word.length() - and so it is attempting to create a substring using String Indexes which are greater than the String's length.
How can I adjust my method so that it only creates substrings of each word until letterNumber == word.length(), and also then be able to apply the sorting algorithm to these shorter words - "a" would become before "aa".

Just group the elements that are shorter than the string length in a additional group. Also you need to sort the least significant (relevant) character first. The following code uses java collections instead of whatever datastructure you were using:
public static void stringRadixSort(List<String> list, int letters) {
if (list.size() <= 1) {
return;
}
List<String>[] buckets = new List[27];
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new LinkedList<>();
}
int largestLength = -1;
int secondLargestLength = 0;
for (String s : list) {
int length = s.length();
if (length >= largestLength) {
secondLargestLength = largestLength;
largestLength = length;
} else if (secondLargestLength < length) {
secondLargestLength = length;
}
}
if (largestLength > letters) {
throw new IllegalArgumentException("one of the strings is too long");
}
for (int i = secondLargestLength == largestLength ? secondLargestLength-1 : secondLargestLength; i >= 0; i--) {
for (String word : list) {
int index = (word.length() <= i) ? 0 : word.charAt(i) - ('a' - 1);
buckets[index].add(word);
}
list.clear();
for (List<String> lst : buckets) {
if (lst != null) {
list.addAll(lst);
lst.clear();
}
}
}
}

Why don't you replace
String letter = word.substring(letterNumber - 1, letterNumber);
char ch = letter.charAt(0);
with
char ch = word.charAt(letterNumber - 1);
which gives you the char directly. But this doesn't solve the problem with the IndexOutOfBoundException.
You should of course catch the exception and handle it. Maybe it is good to create a bucket for this case: When the word is too short for the current iteration, it is sorted into a bucket. When merging the list back together, take the elements of this bucket first.
public static void stringRadixSort(List<String> list, int letters) {
List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 27);
int letterNumber = 1; //Sorts list by 1st letter of each word, then 2nd etc.
for (int i = 0; i < letters; i++) {
while (!list.isEmpty()) {
String word = list.remove(list.first());
if (word.length() > letters) throw new UnsortableException("The list contains a word that holds more letters than the given maximum number of letters."
+ "\nMax Letters: " + letters + "\nWord: " + word);
int index;
if(word.length() > letterNumber) {
char ch = word.charAt(letterNumber - 1);
index = ch - 'a' + 1; //gets index of each letter ('a' = buckets[1], 'z' = buckets[26], buckets[0] is for short words
} else {
index = 0;
}
if (buckets[index] == null) {
buckets[index] = new LinkedList<String>();
}
buckets[index].insertLast(word);
}
for (int j = 0; j < buckets.length; j++) {
if (buckets[j] != null) {
while (!buckets[j].isEmpty()) {
list.insertLast(buckets[j].remove(buckets[j].first()));
}
}
}
letterNumber++;
}
}

Throughout all my attempts, I had been sorting the words by most significant letter first (1st letter of each word), then the next significant, and so on. Of course, Radix sort relies on sorting the least significant digit/letter (the last digit/letter of the number/word). So, instead of iterating through my outer for loop starting by focusing on letterNumber = 1 and incrementing this after each iteration, I instead began with letterNumber = maxWordLength, and then decremented this after each iteration, so that each iteration compares the next most significant letter.
#SuppressWarnings("unchecked")
public static void stringRadixSort(List<String> list) {
List<String>[] buckets = (List<String>[]) Array.newInstance(List.class, 27);
//Find longest word in list
int maxWordLength = 0;
for (String word : list) {
if (word.length() > maxWordLength) {
maxWordLength = word.length();
}
}
//Sorts list based on least significant letter (last letter of word) to most significant
int letterNumber = maxWordLength;
for (int i = 0; i < maxWordLength; i++) {
while (!list.isEmpty()) {
String word = list.remove(list.first());
int index = 0;
if(word.length() >= letterNumber) {
char ch = word.charAt(letterNumber - 1);
index = ch - 'a' + 1; //gets index of each letter ('a' = buckets[1], 'z' = buckets[26], buckets[0] is for words shorter than 'letterNumber')
}
if (buckets[index] == null) {
buckets[index] = new LinkedList<String>();
}
buckets[index].insertLast(word);
}
for (int j = 0; j < buckets.length; j++) {
if (buckets[j] != null) {
while (!buckets[j].isEmpty()) {
list.insertLast(buckets[j].remove(buckets[j].first()));
}
}
}
letterNumber--;
}
}

Related

Generate all possible string combinations by replacing the hidden “#” number sign

My task is to generates all possible combinations of that rows without the hidden # number sign. The input is XOXX#OO#XO and here is the example of what the output should be:
XOXXOOOOXO
XOXXOOOXXO
XOXXXOOOXO
XOXXXOOXXO
I am only allowed to solve this solution iteratively and I am not sure how to fix this and have been working on this code for a week now.
Here is my code:
import java.lang.Math;
public class help {
public static void main(String[] args) {
String str = new String("XOXX#OO#XO");
UnHide(str);
}
public static void UnHide(String str) {
//converting string to char
char[] chArr = str.toCharArray();
//finding all combinations for XO
char[] xo = new char[]{'X', 'O'};
int count = 0;
char perm = 0;
String s = "";
//finding amount of times '#' appears in string
for (int i = 0; i < str.length(); i++) {
if (chArr[i] == '#')
count++;
}
int[] combo = new int[count];
int pMax = xo.length;
while (combo[0] < pMax) {
// print the current permutation
for (int k = 0; k < count; k++) {
//print each character
//System.out.print(xo[combo[i]]);
perm = xo[combo[k]];
s = String.valueOf(perm);
char[] xoArr = s.toCharArray();
String strChar = new String(xoArr);
//substituting '#' to XO combo
for (int i = 0; i < chArr.length; i++) {
for (int j = 0; j < s.length(); j++) {
if (chArr[i] == '#') {
chArr[i] = xoArr[j];
strChar = String.copyValueOf(chArr);
i++;
}
}
i++;
if (i == chArr.length - 1) {
System.out.println(strChar);
i = 0;
}
}
}
System.out.println(); //print end of line
// increment combo
combo[count - 1]++; // increment the last index
//// if increment overflows
for (int i = count - 1; combo[i] == pMax && i > 0; i--) {
combo[i - 1]++; // increment previous index
combo[i] = 0; // set current index to zero
}
}
}
}
Since your input has 2 #'s, there are 2n = 4 permutations.
If you count from 0 to 3, and look at the numbers in binary, you get 00, 01, 10, and 11, so if you use that, inserting O for 0 and X for 1, you can do this using simple loops.
public static void unHide(String str) {
int count = 0;
for (int i = 0; i < str.length(); i++)
if (str.charAt(i) == '#')
count++;
if (count > 30)
throw new IllegalArgumentException("Too many #'s found. " + count + " > 30");
char[] buf = str.toCharArray();
for (int permutation = 0, end = 1 << count; permutation < end; permutation++) {
for (int i = buf.length - 1, bit = 0; i >= 0; i--)
if (str.charAt(i) == '#')
buf[i] = "OX".charAt(permutation >>> bit++ & 1);
System.out.println(buf);
}
}
Test
unHide("XOXX#OO#XO");
Output
XOXXOOOOXO
XOXXOOOXXO
XOXXXOOOXO
XOXXXOOXXO
You can iteratively generate all possible combinations of strings using streams as follows:
public static String[] unHide(String str) {
// an array of substrings around a 'number sign'
String[] arr = str.split("#", -1);
// an array of possible combinations
return IntStream
// iterate over array indices
.range(0, arr.length)
// append each substring with possible
// combinations, except the last one
// return Stream<String[]>
.mapToObj(i -> i < arr.length - 1 ?
new String[]{arr[i] + "O", arr[i] + "X"} :
new String[]{arr[i]})
// reduce stream of arrays to a single array
// by sequentially multiplying array pairs
.reduce((arr1, arr2) -> Arrays.stream(arr1)
.flatMap(str1 -> Arrays.stream(arr2)
.map(str2 -> str1 + str2))
.toArray(String[]::new))
.orElse(null);
}
// output to the markdown table
public static void main(String[] args) {
String[] tests = {"XOXX#OOXO", "XOXX#OO#XO", "#XOXX#OOXO#", "XO#XX#OO#XO"};
String header = String.join("</pre> | <pre>", tests);
String matrices = Arrays.stream(tests)
.map(test -> unHide(test))
.map(arr -> String.join("<br>", arr))
.collect(Collectors.joining("</pre> | <pre>"));
System.out.println("| <pre>" + header + "</pre> |");
System.out.println("|---|---|---|---|");
System.out.println("| <pre>" + matrices + "</pre> |");
}
XOXX#OOXO
XOXX#OO#XO
#XOXX#OOXO#
XO#XX#OO#XO
XOXXOOOXOXOXXXOOXO
XOXXOOOOXOXOXXOOOXXOXOXXXOOOXOXOXXXOOXXO
OXOXXOOOXOOOXOXXOOOXOXOXOXXXOOXOOOXOXXXOOXOXXXOXXOOOXOOXXOXXOOOXOXXXOXXXOOXOOXXOXXXOOXOX
XOOXXOOOOXOXOOXXOOOXXOXOOXXXOOOXOXOOXXXOOXXOXOXXXOOOOXOXOXXXOOOXXOXOXXXXOOOXOXOXXXXOOXXO
The process would probably be best to calculate the number of permutations, then loop through each to define what combination of characters to use.
For that, we'll have to divide the permutation number by some value related to the index of the character we're replacing, which will serve as the index of the character to swap it to.
public static void test(String word) {
// Should be defined in class (outside method)
String[] replaceChars = {"O", "X"};
char replCharacter = '#';
String temp;
int charIndex;
int numReplaceable = 0;
// Count the number of chars to replace
for (char c : word.toCharArray())
if (c == replCharacter)
numReplaceable++;
int totalPermutations = (int) Math.pow(replaceChars.length, numReplaceable);
// For all permutations:
for (int permNum = 0; permNum < totalPermutations; permNum++) {
temp = word;
// For each replacement character in the word:
for (int n = 0; n < numReplaceable; n++) {
// Calculate the character to swap the nth replacement char to
charIndex = permNum / (int) (Math.pow(replaceChars.length, n))
% replaceChars.length;
temp = temp.replaceFirst(
replCharacter + "", replaceChars[charIndex]);
}
System.out.println(temp);
}
}
Which can produces:
java Test "#TEST#"
OTESTO
XTESTO
OTESTX
XTESTX
This can also be used with any number of characters, just add more to replaceChars.

Function is working in some cases but fails when longest sub-string "reuses" a character

I have a function called lengthOfLongestSubstring and its job is to find the longest substring without any repeated characters. For the most part, it works, but when it gets an input like "dvdf" it prints out 2 (rather than 3) and gives [dv, df] when it should be [d, vdf].
So, I first go through the string and see if there are any unique characters. If there are, I append it to the ans variable. (I think this is the part that needs some fixing). If there is a duplicate, I store it in the substrings linked list and reset the ans variable to the duplicate string.
Once the whole string has been traversed, I find the longest substring and return its length.
public static int lengthOfLongestSubstring(String s) {
String ans = "";
int len = 0;
LinkedList<String> substrings = new LinkedList<String>();
for (int i = 0; i < s.length(); i++) {
if (!ans.contains("" + s.charAt(i))) {
ans += s.charAt(i);
} else {
substrings.add(ans);
ans = "" + s.charAt(i);
}
}
substrings.add(ans); // add last seen substring into the linked list
for (int i = 0; i < substrings.size(); i++) {
if (substrings.get(i).length() >= len)
len = substrings.get(i).length();
}
System.out.println(Arrays.toString(substrings.toArray()));
return len;
}
Here are some test results:
//correct
lengthOfLongestSubstring("abcabcbb") -> 3 ( [abc, abc, b, b])
lengthOfLongestSubstring("pwwkew") -> 3 ([pw, wke, w]).
lengthOfLongestSubstring("ABDEFGABEF"); -> 6 ([ABDEFG, ABEF])
// wrong
System.out.println(lengthOfLongestSubstring("acadf")); -> 3, ([ac, adf]) *should be 4, with the linked list being [a, cadf]
Any suggestions to fix this? Do I have to redo all my logic?
Thanks!
You code is mistakenly assuming that when you find a repeated character, the next candidate substring starts at the repeated character. That is not true, it starts right after the original character.
Example: If string is "abcXdefXghiXjkl", there are 3 candidate substrings: "abcXdef", "defXghi", and "ghiXjkl".
As you can see, the candidate substrings ends before a repeating character and starts after a repeating character (and begin and end of string).
So, when you find a repeating character, the position of the previous instance of that character is needed to determine the start of the next substring candidate.
The easiest way to handle that, is to build a Map of character to last seen position. That will also perform faster than continually performing substring searches to check for repeating character, like the question code and the other answers are doing.
Something like this:
public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> charPos = new HashMap<>();
List<String> candidates = new ArrayList<>();
int start = 0, maxLen = 0;
for (int idx = 0; idx < s.length(); idx++) {
char ch = s.charAt(idx);
Integer preIdx = charPos.get(ch);
if (preIdx != null && preIdx >= start) { // found repeat
if (idx - start > maxLen) {
candidates.clear();
maxLen = idx - start;
}
if (idx - start == maxLen)
candidates.add(s.substring(start, idx));
start = preIdx + 1;
}
charPos.put(ch, idx);
}
if (s.length() - start > maxLen)
maxLen = s.length() - start;
if (s.length() - start == maxLen)
candidates.add(s.substring(start));
System.out.print(candidates + ": ");
return maxLen;
}
The candidates is only there for debugging purposes, and is not needed, so without that, the code is somewhat simpler:
public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> charPos = new HashMap<>();
int start = 0, maxLen = 0;
for (int idx = 0; idx < s.length(); idx++) {
char ch = s.charAt(idx);
Integer preIdx = charPos.get(ch);
if (preIdx != null && preIdx >= start) { // found repeat
if (idx - start > maxLen)
maxLen = idx - start;
start = preIdx + 1;
}
charPos.put(ch, idx);
}
return Math.max(maxLen, s.length() - start);
}
Test
System.out.println(lengthOfLongestSubstring(""));
System.out.println(lengthOfLongestSubstring("x"));
System.out.println(lengthOfLongestSubstring("xx"));
System.out.println(lengthOfLongestSubstring("xxx"));
System.out.println(lengthOfLongestSubstring("abcXdefXghiXjkl"));
System.out.println(lengthOfLongestSubstring("abcabcbb"));
System.out.println(lengthOfLongestSubstring("pwwkew"));
System.out.println(lengthOfLongestSubstring("ABDEFGABEF"));
Output (with candidate lists)
[]: 0
[x]: 1
[x, x]: 1
[x, x, x]: 1
[abcXdef, defXghi, ghiXjkl]: 7
[abc, bca, cab, abc]: 3
[wke, kew]: 3
[ABDEFG, BDEFGA, DEFGAB]: 6
Instead of setting ans to the current char when a character match is found
ans = "" + s.charAt(i);
You should add the current char to all the characters after the first match of the current char
ans = ans.substring(ans.indexOf(s.charAt(i)) + 1) + s.charAt(i);
The full method thus becomes
public static int lengthOfLongestSubstring(String s) {
String ans = "";
int len = 0;
LinkedList<String> substrings = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
if (!ans.contains("" + s.charAt(i))) {
ans += s.charAt(i);
} else {
substrings.add(ans);
// Only the below line changed
ans = ans.substring(ans.indexOf(s.charAt(i)) + 1) + s.charAt(i);
}
}
substrings.add(ans); // add last seen substring into the linked list
for (int i = 0; i < substrings.size(); i++) {
if (substrings.get(i).length() >= len)
len = substrings.get(i).length();
}
System.out.println(Arrays.toString(substrings.toArray()));
return len;
}
Using this code the acceptance criteria you specified passed successfully
//correct
lengthOfLongestSubstring("dvdf") -> 3 ( [dv, vdf])
lengthOfLongestSubstring("abcabcbb") -> 3 ([abc, bca, cab, abc, cb, b])
lengthOfLongestSubstring("pwwkew") -> 3 ([pw, wke, kew]).
lengthOfLongestSubstring("ABDEFGABEF"); -> 6 ([ABDEFG, BDEFGA, DEFGAB, FGABE, GABEF])
lengthOfLongestSubstring("acadf"); -> 4 ([ac, cadf])
Create a nested for loop to check at each index in the array.
public static int lengthOfLongestSubstring(String s) {
String ans = "";
int len = 0;
LinkedList<String> substrings = new LinkedList<String>();
int k = 0;
for (int i = 0; i < s.length(); i++) {
if(k == s.length()) {
break;
}
for(k = i; k < s.length(); k++) {
if (!ans.contains("" + s.charAt(k))) {
ans += s.charAt(k);
} else {
substrings.add(ans);
ans = "";
break;
}
}
}
substrings.add(ans); // add last seen substring into the linked list
for (int i = 0; i < substrings.size(); i++) {
if (substrings.get(i).length() >= len)
len = substrings.get(i).length();
}
System.out.println(Arrays.toString(substrings.toArray()));
return len;
}
Example:
lengthOfLongestSubstring("ABDEFGABEF"); -> 6 ([ABDEFG, BDEFGA, DEFGAB, EFGAB, FGABE, GABEF])

how to solve the indexing in java programming language

public static int indexOf(String text , char index){
char array [] = new char[text.length()];
for(int i = 0 ; i < text.length(); i++){
array[i] = text.charAt(i);
}// end of first loop
// the above loop converts the string obj to char array
for(int i = 0 ; i < array.length; i++){
if(array[i] == index){ // if a given letter is exist in string
System.out.println("The index of " + index + " is " + i);
return i; // show or return the index of that given letter
}
}//end of second loop
System.out.println("The Letter you Entered does not Exist");
return -1;
}// end of method
this piece of code is for finding the index of string ;
first it takes a string and a character as an input than it convert it to array of characters than it search for an given lettter if it finds it.
method will return it's index but the main problem is that we have more than one same letter in a string so it will return the first one .
and how could i detect the second one or how I can differentiate the second same letter and show it is index for example:
indexOf("kingJoker",'k');
here in kingjoker string we have two k letter the method can't find second k index so that is the problem.
Change the return type of your method to int[]
Walk the string once to count the matches
Make an array of the size equal to the count
Walk the string again, populating the return indexes as you go.
Here is your modified implementation:
public static int[] indexesOf(String text , char ch) {
int count = 0;
for (int i = 0 ; i != text.length() ; i++) {
if (text.charAt(i) == ch) {
count++;
}
}
int[] indexes = new int[count];
int pos = 0;
for (int i = 0 ; i != text.length() ; i++) {
if (text.charAt(i) == ch) {
System.out.println("Found '" + ch + "' at " + i);
indexes[pos++] = i;
}
}
return indexes;
}
To detect multiple letters, I recommend you store every index within a List<Integer>. You can use the following (courtesy of smac89's answer):
public static List<Integer> indexOf(String text, char index) {
return IntStream.range(0, text.length())
.filter(i -> s.charAt(i) == index)
.boxed()
.collect(Collectors.toList());
}

Java program to find the letter that appears in the most words?

I have a sentence, and I want to find the char that appears in the most words, and how many words it appears in.
For example: "I like visiting my friend Will, who lives in Orlando, Florida."
Which should output I 8.
This is my code:
char maxChar2 = '\0';
int maxCount2 = 1;
for (int j=0; j<strs2.length; j++) {
int charCount = 1;
char localChar = '\0';
for (int k=0; k<strs2[j].length(); k++) {
if (strs2[j].charAt(k) != ' ' && strs2[j].charAt(k) != maxChar2) {
for (int l=k+1; l<strs2[j].length(); l++) {
if (strs2[j].charAt(k)==strs2[j].charAt(l)) {
localChar = strs2[j].charAt(k);
charCount++;
}
}
}
}
if (charCount > maxCount2) {
maxCount2 = charCount;
maxChar2 = localChar;
}
}
, where strs2 is a String array.
My program is giving me O 79. Also, uppercase and lowercase do not matter and avoid all punctuation.
As a tip, try using more meaningful variable names and proper indentation. This will help a lot especially when your program is not doing what you thought it should do. Also starting smaller and writing some tests for it will help a bunch. Instead of a full sentence, get it working for 2 words, then 3 words, then a more elaborate sentence.
Rewriting your code to be a bit more readable:
// Where sentence is: "I like".split(" ");
private static void getMostFrequentLetter(String[] sentence) {
char mostFrequentLetter = '\0';
int mostFrequentLetterCount = 1;
for (String word : sentence) {
int charCount = 1;
char localChar = '\0';
for (int wordIndex = 0; wordIndex < word.length(); wordIndex++) {
char currentLetter = word.charAt(wordIndex);
if (currentLetter != ' ' && currentLetter != mostFrequentLetter) {
for (int l = wordIndex + 1; l < word.length(); l++) {
char nextLetter = word.charAt(l);
if (currentLetter == nextLetter) {
localChar = currentLetter;
charCount++;
}
}
}
}
if (charCount > mostFrequentLetterCount) {
mostFrequentLetterCount = charCount;
mostFrequentLetter = localChar;
}
}
}
Now all I did was rename your variables and change your for loop to a for-each loop. By doing this you can see more clearly your algorithm and what you're trying to do. Basically you're going through each word and comparing the current letter with the next letter to check for duplicates. If I run this with "I like" i should get i 2 but instead I get null char 1. You aren't properly comparing and saving common letters. This isn't giving you the answer, but I hope this makes it more clear what your code is doing so you can fix it.
Here is a somewhat more elegant solution
public static void FindMostPopularCharacter(String input)
{
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
input = input.toUpperCase();
HashMap<Character, Integer> charData = new HashMap<>();
char occursTheMost = 'A'; //start with default most popular char
int maxCount = 0;
//create the map to store counts of all the chars seen
for(int i = 0; i < alphabet.length(); i++)
charData.put(alphabet.charAt(i), 0);
//first find the character to look for
for(int i = 0; i < input.length(); i++)
{
char c = input.charAt(i);
//if contained in our map increment its count
if(charData.containsKey(c))
charData.put(c, charData.get(c) + 1);
//check for a max count and set the values accordingly
if(charData.containsKey(c) && charData.get(c) > maxCount)
{
occursTheMost = c;
maxCount = charData.get(c);
}
}
//final step
//now split it up into words and search which contain our most popular character
String[] words = input.split(" ");
int wordCount = 0;
CharSequence charSequence;
for(Character character : charData.keySet())
{
int tempCount = 0;
charSequence = "" + character;
for(int i = 0; i < words.length; i++)
{
if(words[i].contains(charSequence))
tempCount++;
}
if(tempCount > wordCount)
{
occursTheMost = character;
wordCount = tempCount;
}
}
System.out.println(occursTheMost + " " + wordCount);
}
Output of
String input = "I like visiting my friend Will, who lives in Orlando, Florida.";
FindMostPopularCharacter(input);
is
I 8
Note: If there are ties this will only output the character that first reaches the maximum number of occurrences.
FindMostPopularCharacter("aabb aabb aabb bbaa");
Outputs
B 4
because B reaches the max first before A due to the last word in the input.
FindMostPopularCharacter("aab aab b")
B 3

How to sort integer array without losing location

I'm making a program which receives a string as input and returns the "sum" of the values for each letter of each word.
For example, my input of "Take advantage, do your best, don't stress.", would return:
do(19) take(37) dont(43) best(46) advantage(75) your(79) stress(100)
"do" would have a value of 19 because the letter "d" has a value of 4 (it is the fourth letter of the alphabet), and "o" has a value of 15, so the total is 19.
Now to store these values I have two arrays, one string array for each word, and one int array for the point value that they have. However, I only have this so far:
take(37) advantage(75) do(19) your(79) best(46) dont(53) stress(100)
As you can see, it is not sorted in ascending order as I am trying to do. I display these values like this:
System.out.print(words[j] + "(" + points[j] + ")" + " ");
where words is the String array and points is the int array. How can I sort them?
My current code:
public static void main (String[] args)
{
String input = "Take advantage, do your best, don't stress.";
String output = "";
//Get rid of all punctuation
for(int i = 0; i < input.length(); i++){
if( ( (int)input.charAt(i) >= 65 && (int)input.charAt(i) <= 90) || (int)input.charAt(i) == 32 || ( (int)input.charAt(i) >= 97 && (int)input.charAt(i) <= 122)){
//Handles Uppercase
if(input.charAt(i) >= 65 && input.charAt(i) <= 90){
int temp = (int)input.charAt(i) + 32;
char c = (char)temp;
output += c;
}
//Handles all other characters
else{
output += input.charAt(i);
}
}
}
//Done punctuation
String[] words = output.split(" ");
int[] points = new int[words.length];
//Points assignment
for(int j = 0; j < words.length; j++){
for(int k = 0; k < words[j].length(); k++){
points[j] += (int)words[j].charAt(k) - 96;
}
System.out.print(words[j] + "(" + points[j] + ")" + " ");
}
}
How about storing your results in a Map<String,Integer> instead of two lists:
Map myMap = new HashMap<String,Integer>;
From there you can sort the Map by its values: Sort a Map<Key, Value> by values (Java)
Next you can iterate through the sorted map:
for(String s : myMap.keySet()){
System.out.println(s+"("+myMap.get(s)+")");
}
If that is an option, your code can be made much simpler with Java 8.
First of all, removing punctuation can be done with a simple regular expression: you only want to keep letters, so we can just remove everything that is neither a letter nor a space. This is done by calling replaceAll("[^a-zA-Z\\s]", ""). After that, we can get a hold of all the words by splitting around "\\s+" (i.e. all whitespace characters).
Then, let's create a helper method returning the value of a String. As you have in your question, this would just be:
private static int value(String str) {
return str.chars().map(c -> c - 'a' + 1).sum();
}
Finally, we need to sort the words array with a comparator comparing the value of each word. The comparator is created with the help of Comparator.comparingInt(keyExtractor) where the key extraction would be a function returning the value of a word. In this case, it could be expressed a lambda expression: word -> value(word).
To have the final output, we need to transform the words array into a String where each word is concatenated with its value in parentheses. This is done by creating a Stream<String> of the words (Arrays.stream(array)), sorting it according the comparator above (sorted(comparator)), mapping each word to the result of concatenating its value to it and finally collecting that into a String delimited by a space (Collectors.joining(delimiter)).
Whole code would be:
public static void main(String[] args) {
String str = "Take advantage, do your best, don't stress.";
String[] words = str.toLowerCase().replaceAll("[^a-zA-Z\\s]", "").split("\\s+");
String output =
Arrays.stream(words)
.sorted(Comparator.comparingInt(w -> value(w)))
.map(w -> w + "(" + value(w) + ")")
.collect(Collectors.joining(" "));
System.out.println(output);
}
private static int value(String str) {
return str.chars().map(c -> c - 'a' + 1).sum();
}
Use any of sorting algorithm and do sorting for both arrays. For example:
public static void bubbleSort(int[] numArray, String[] words) {
int n = numArray.length;
int temp = 0;
String tt;
for (int i = 0; i < n; i++) {
for (int j = 1; j < (n - i); j++) {
if (numArray[j - 1] > numArray[j]) {
temp = numArray[j - 1];
tt=words[j-1];
numArray[j - 1] = numArray[j];
words[j-1]=words[j];
numArray[j] = temp;
words[j]=tt;
}
Then change last part of your main function to look like this:
String[] words = output.split(" ");
int[] points = new int[words.length];
//Points assignment
for(int j = 0; j < words.length; j++){
for(int k = 0; k < words[j].length(); k++){
points[j] += (int)words[j].charAt(k) - 96;
}
}
bubbleSort(points,words);
for(int j = 0; j < words.length; j++){
System.out.print(words[j] + "(" + points[j] + ")" + " ");
}
If you are not allowed to use Java 8 (else use #Tunaki's approach), create a Comparable object that keeps two values, a String (word) and an int (sum). Then, just add each word to a list and sort it using Collections.sort(yourList).
public class Word implements Comparable<Word>{
private String word;
private int sum;
public Word(String word) {
this.word = word;
setSum();
}
private void setSum() {
//your sum function, I just copy and paste it from your post
for(int k = 0; k < word.length(); k++)
sum += (int)word.charAt(k) - 96;
}
public String getWord() {
return word;
}
public int getSum() {
return sum;
}
#Override
public int compareTo(Word o) {
return this.sum > o.sum ? 1 : -1;
}
}

Categories