Count occurrences of each unique character - java

How to find the number of occurrence of every unique character in a String? You can use at most one loop. please post your solution, thanks.

Since this sounds like a homework problem, let's try to go over how to solve this problem by hand. Once we do that, let's see how we can try to implement that in code.
What needs to be done?
Let's take the following string:
it is nice and sunny today.
In order to get a count of how many times each character appears in the above string, we should:
Iterate over each character of the string
Keep a tally of how many times each character in the string appears
How would we actually try it?
Doing this this by hand might be like this:
First, we find a new characeter i, so we could note that in a table and say that i appeared 1 time so far:
'i' -> 1
Second, we find another new character t, so we could add that in the above table:
'i' -> 1
't' -> 1
Third, a space, and repeat again...
'i' -> 1
't' -> 1
' ' -> 1
Fourth, we encounter an i which happens to exist in the table already. So, we'll want to retrieve the existing count, and replace it with the existing count + 1:
'i' -> 2
't' -> 1
' ' -> 1
And so on.
How to translate into code?
Translating the above to code, we may write something like this:
For every character in the string
Check to see if the character has already been encountered
If no, then remember the new character and say we encountered it once
If yes, then take the number of times it has been encountered, and increment it by one
For the implementation, as others have mentioned, using a loop and a Map could achieve what is needed.
The loop (such as a for or while loop) could be used to iterate over the characters in the string.
The Map (such as a HashMap) could be used to keep track of how many times a character has appeared. In this case, the key would be the character and the value would be the count for how many times the character appears.
Good luck!

It's a homework, so cannot post the code, but here is one approach:
Iterate through the string, char by char.
Put the char in a hashmap key and initialize its value to 1 (count). Now, if the char is encountered again, update the value (count+1). Else add the new char to key and again set its value (count=1)

Here you go! I have done a rough program on Count occurrences of each unique character
public class CountUniqueChars{
public static void main(String args[]){
HashMap<Character, Integer> map;
ArrayList<HashMap<Character, Integer>> list = new ArrayList<HashMap<Character,Integer>>();
int i;
int x = 0;
Boolean fire = false;
String str = "Hello world";
str = str.replaceAll("\\s", "").toLowerCase();
System.out.println(str.length());
for(i=0; i<str.length() ; i++){
if(list.size() <= 0){
map = new HashMap<Character, Integer>();
map.put(str.charAt(i), 1);
list.add(map);
}else{
map = new HashMap<Character, Integer>();
map.put(str.charAt(i), 1);
fire = false;
for (HashMap<Character, Integer> t : list){
if(t.containsKey(str.charAt(i)) == map.containsKey(str.charAt(i))){
x = list.indexOf(t);
fire = true;
map.put(str.charAt(i), t.get(str.charAt(i))+1);
}
}
if(fire){
list.remove(x);
}
list.add(map);
}
}
System.out.println(list);
}
}

Related

How to remove a specific String from a String ArrayList

I have an ArrayList that contains a bunch of words each in their own cell that come from a file. Some of those words are complete word like "physicist, water, gravity". However, other words are just letters that got split throughout the process of the program. For example, "it's" became "it" and "s". As such, I want to remove all of the single letter words except "I" and "A" because these are actual words.
This is the code I have for now:
for(int i=0;i<dictionnary.size();i++) {
if(dictionnary.get(i).compareToIgnoreCase("I")!=0||dictionnary.get(i).compareToIgnoreCase("A")!=0||dictionnary.get(i).length()==1){
dictionnary.remove(i);
}
}
Where dictionnary is my ArrayList. However, when I print out the content of my arrayList the "s" from it's remains. I also know that there was originally a word "E" that got removed throughout the process above. I'm confused as to why the "S" remains and how to fix it.
From my understanding this code goes through the ArrayList and checks if the length of the case is 1 (which is the case for all single letter words) as well as checking if that case is a case of "I" or "A" regardless of if it is capitalized or not. It then removes the cases that don't correspond to the "I" or "A".
Consider using the Collection Iterator for safe removal of elements during iteration.
for (Iterator<String> iter = dictionary.iterator() ; iter.hasNext() ; ) {
String word = iter.next();
if (word.length() == 1
&& !"I".equals(word)
&& !"A".equalsIgnoreCase(word)) {
iter.remove();
}
}
My suggestion is the following:
You can use removeIf in a next way.
removeIf takes a predicate.
public static void main(String[] args) {
List<String> dictionary = new ArrayList<>();
dictionary.add("I");
dictionary.add("A");
dictionary.add("p");
dictionary.add("its");
dictionary.add("water");
dictionary.add("s");
Integer sizeRemove =1;
dictionary.removeIf(
word ->
!"I".equals(word)
&& !"A".equalsIgnoreCase(word)
&& word.length() == sizeRemove
);
System.out.println(dictionary);
}
The output is the following:
[I, A, its, water]
Reference:
https://www.programiz.com/java-programming/library/arraylist/removeif
Use iterators instead. Let's say you have a list of (1,2,3,4,5) and you want to remove the numbers 2 and 3. You start looping through and get to the second element 2. Here your i is 1. You remove that element and go to i=2. What you have now is (1,3,4,5). Since i=2, you have missed one element.
And that's the reason you should use iterators instead. Refer to #vsfDawg answer.

Hash Tables: Ransom Note hackerrank

Harold is a kidnapper who wrote a ransom note, but now he is worried it will be traced back to him through his handwriting. He found a magazine and wants to know if he can cut out whole words from it and use them to create an untraceable replica of his ransom note. The words in his note are case-sensitive and he must use only whole words available in the magazine. He cannot use substrings or concatenation to create the words he needs.
Given the words in the magazine and the words in the ransom note, print Yes if he can replicate his ransom note exactly using whole words from the magazine; otherwise, print No.
For example, the note is "Attack at dawn". The magazine contains only "attack at dawn". The magazine has all the right words, but there's a case mismatch. The answer is .
Sample Input 0
6 4
give me one grand today night
give one grand today
Sample Output 0
Yes
Sample Input 1
6 5
two times three is not four
two times two is four
Sample Output 1
No
My code 5/22 test cases failed :(
I can't figure out why 5 failed.
static void checkMagazine(String[] magazine, String[] note) {
int flag = 1;
Map<String, Integer> wordMap = new HashMap<>();
for(String word: magazine) {
if(!wordMap.containsKey(word)) {
wordMap.put(word, 1);
} else
wordMap.put(word,wordMap.get(word)+1);
}
for(String word: note){
if(!wordMap.containsKey(word)){
flag = 0;
break;
}
else wordMap.remove(word, wordMap.get(word));
}
if(flag == 0)
System.out.println("No");
else
System.out.println("Yes");
}
It's probably because instead of decrementing the count of the words in the magazine when you retrieve one, you're removing all counts of that word completely. Try this:
for(String word: note){
if(!(wordMap.containsKey(word) && wordMap.get(word) > 0)){
flag = 0;
break;
}
else wordMap.put(word, wordMap.get(word)-1);
}
wordMap is a frequency table and gives word counts.
However for every word in the note, you must decrease the word count instead of entirely removing the entry. Only when the word count reaches 0 one could remove the entry.
An other isssue is the case-sensitivity. Depending on the requirements you may need to convert all words to lowercase.
else {
wordMap.computeIfPresent(word, (k, v) -> v <= 1? null : v - 1);
}
This checks that the old value v is above 1 and then decreases it, or else returns a null value signaling to delete the entry.
The frequency counts can be done:
Map<String, Integer> wordMap = new HashMap<>();
for(String word: magazine) {
wordMap.merge(word, 1, Integer::sum);
}
I think, this implementation is simplier
static boolean checkMagazine(String[] magazine, String[] note) {
List<String> magazineCopy = new ArrayList<>(Arrays.asList(magazine));
for (String word : note)
{
if (magazineCopy.contains(word)) {
magazineCopy.remove(word);
continue;
}
return false;
}
return true;
}
I suppose your error is here:
else wordMap.remove(word, wordMap.get(word));
you are removing the word from the map, instead of decreasing the number of such words and only if the number reaches 0, you should remove the word from the map.
Python Solution
def checkMagazine(magazine, ransom):
magazine.sort()
ransom.sort()
for word in ransom:
if word not in magazine:
flag = False
break
else:
magazine.remove(word)
flag = True
if (flag):
print("Yes")
else:
print("No")

Counting Common Elements in a String Array

I am trying to make a program to count common elements occuring in all the Strings in a String[] array. I have the following:-
A master array and a flag array both of size 26
Now for each string: I am marking frequency 1 for each character that appears in the string without incrementing in flag array.
Now I am adding the values of flag array to corresponding values of master array
my code looks like this
for(String str : arr)
{
for(char ch : str.toCharArray())
{
flag[ch - 97] = 1;
master[ch - 97] =master[ch -97] + flag[ch - 97];
}
}
My plan is to finally count elements in the master array that have value equal to input string array's length. This count will represent the count of characters that are common to all the strings
But my code has a flaw.
if a String has duplicate elements for example, 'ball' (with 2 ls). The corresponding value of the element in master array is getting incremented again.
Which makes its value larger than what I wanted.
So this is what I did.
for(String str : arr)
{
newstr = ""; //to keep track of each character in the string
for(char ch : str.toCharArray())
{
int counter = 0;
for(int i = 0; i < newstr.length();i++)
{
char ch2 = newstr.charAt(i);
if (ch == ch2 )
{
counter = counter + 1; //if duplicate
break;
}
}
if(counter == 1)
{
break;
}
flag[ch - 97] = 1;
master[ch - 97] =master[ch -97] + flag[ch - 97];
newstr = newstr + ch;
}
}
Is this the right approach? or could this code be more optimized?
IMHO - "The right approach" is one you fully understand and can refactor at will. There are generally always multiple ways to approach solving any programming problem. Personally, I would approach (what I think is) the problem you are trying to solve in a manner more ideomatic to Java.
For the entire array of strings you are going to examine, every character in the first string you examine is in every string examined so far, so every character in that first string would go into a Map<Character, Integer> i.e. charCountMap.put(aChar, 1) For the second string and every string thereafter: If a character in the string under examination is in the map's keySet, then increment the associated Integer (increment that key's associated value) charCountMap.get(aChar)++. After examining every character in every string, then the keys in the keyset that map to Integers with values that match the original string array's length are exactly the characters that were found in every string.
So far, this proposed solution doesn't solve the repeating character problem you describe above. To solve that part, I think you need to keep a separate list of unique characters "seen so far" in the "string under examination" (and empty the list for every new string). You would check the "seen so far" list first, and skip all further processing of that character if found in "seen so far", only characters not "seen so far" (in this string under examination) would be checked against the map's keyset. example code
There is also a recursive approach to programming a solution to this problem, but I'll leave that fruit hanging low...

First non-repeating character in a stream

My answer to this question is as follows, but I want to know if I can use this code and what will be the complexity:
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class FirstNonRepeatingCharacterinAString {
private char firstNonRepeatingCharacter(String str) {
LinkedHashMap<Character, Integer> hash =
new LinkedHashMap<Character, Integer>();
for(int i = 0 ; i< str.length() ; i++)
{
if(hash.get(str.charAt(i))==null)
hash.put(str.charAt(i), 1);
else
hash.put(str.charAt(i), hash.get(str.charAt(i))+1);
}
System.out.println(hash.toString());
for(Entry<Character, Integer> c : hash.entrySet())
{
if(c.getValue() == 1)
return c.getKey();
}
return 0 ;
}
public static void main(String args[])
{
String str = "geeksforgeeks";
FirstNonRepeatingCharacterinAString obj =
new FirstNonRepeatingCharacterinAString();
char c = obj.firstNonRepeatingCharacter(str);
System.out.println(c);
}
}
Your question about whether you "can use this code" is a little ambiguous - if you wrote it, I'd think you can use it :)
As for the complexity, it is O(n) where n is the number of characters in the String. To count the number of occurrences, you must iterate over the entire String, plus iterate over them again to find the first one with a count of 1. In the worst case, you have no non-repeating characters, or the only non-repeating character is the last one. In either case, you have to iterate over the whole String once more. So it's O(n+n) = O(n).
EDIT
There is a bug in your code, by the way. Because you are using an insertion-order LinkedHashMap, each call to put(Character,Integer) results in a re-ordering of the underlying list. You should probably use a LinkedHashMap<Character,int[]> instead, and check for the presence of keys before putting. If they exist, then merely increment the value stored in the int[] to avoid re-ording the map by making another put call. Even so, the resulting list will be in reverse order from the way you iterate over it, so the first non-repeating character will be the last one you find when iterating over it whose value is 1. Alternatively, you could just iterate in reverse in your first for loop, then you avoid having to always go through the entire Entry set if the first non-repeating character comes sooner than the final character in the original String.

Get all characters from a string with their number

How in Java can I get list of all characters appearing in string, with number of their appearances ? Let's say we have a string "I am really busy right now" so I should get :
i-2, a-2, r-2, m-1 and so on.
Just have a mapping of every character and their counts. You can get the character array of a String using String#toCharArray() and loop through it using the enhanced for loop. On every iteration, get the count from the mapping, set it if absent and then increment it with 1 and put back in map. Pretty straightforward.
Here's a basic kickoff example:
String string = "I am really busy right now";
Map<Character, Integer> characterCounts = new HashMap<Character, Integer>();
for (char character : string.toCharArray()) {
Integer characterCount = characterCounts.get(character);
if (characterCount == null) {
characterCount = 0;
}
characterCounts.put(character, characterCount + 1);
}
To learn more about maps, check the Sun tutorial on the subject.
You commented that it's "for a project", but it's however a typical homework question because it's pretty basic and covered in the first chapters of a decent Java book/tutorial. If you're new to Java, I suggest to get yourself through the Sun Trails Covering the Basics.
Is it homework? Without knowing it I'll assume a best-effort answer.
The logic behind your problem is to
go trought the list one character at time
count that character: since possible characters (excluding unicode) are just 256 you can have an array of 256 ints and count them there: in this way you won't need to search the correct counter but just increment the right index.
I'm not sure of your exact needs but it seems you want to count occurrences regardless of the case, maybe also ignore characters such as whitespace, etc. So you might want something like this:
String initial = "I am really busy right now";
String cleaned = initial.replaceAll("\\s", "") //remove all whitespace characters
.toLowerCase(); // lower all characters
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (char character : cleaned.toCharArray()) {
Integer count = map.get(character);
count = (count!=null) ? count + 1 : 1;
map.put(character, count);
}
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
Tweak the regex to meet your exact requirements (to skip punctuation, etc).
String input = "AAZERTTYAATY";
char[] chars = input.toCharArray();
Map<Character, Integer> map = new HashMap<>();
for (char aChar : chars) {
Integer charCount = map.putIfAbsent(aChar, 1);
if (charCount != null) {
charCount++;
map.put(aChar, charCount);
}
}

Categories