I have a class that determines how many matching characters are in 2 char arrays. Using the HashSet
contains method, if one char array contains a Character from the second array, display the character.
The problem is if we have two matching characters appearing in multiple locations.
For example, if array 1 = adcd
and array 2 = a05ddd
, d
appears 3 times, not 2.
How would I modify this to account for the correct character count? The code produces "addd"
when it should produce "add"
For the incorrect characters, the result would be
"a--dd"
HashSet<Character> hash = new HashSet<Character>();
String word1 = "adcd";
String word2 = "a05ddd";
char[] ch1 = word1.toCharArray();
char[] ch2 = word2.toCharArray();
Character character = null;
String charLocation = "";
int count = 0;
for (int i = 0; i < ch1.length; i++)
{
hash.add(ch1[i]);
}
for (int i = 0; i < ch2.length; i++)
{
character = ch2[i];
if (hash.contains(character))
{
charLocation = charLocation + character;
count++;
}
if (!hashSet.contains(character))
correctCharPlacements = correctCharPlacements + "-";
}
It is very likely that the data about the frequency of the characters needs to be collected -- so set should be replaced with a map. Then the resulting string may be built with regard to common characters and minimal frequency of the character in both words:
// helper method to build frequency map for a word/string
private static Map<Character, Integer> freqMap(String word) {
return word.chars()
.mapToObj(c -> (char)c)
.collect(Collectors.toMap(x -> x, x -> 1, Integer::sum, LinkedHashMap::new));
}
static String common(String w1, String w2) {
if (null == w1 || null == w2) {
return null;
}
Map<Character, Integer> map1 = freqMap(w1);
Map<Character, Integer> map2 = freqMap(w2);
Set<Character> commonChars = new LinkedHashSet<>(map1.keySet());
commonChars.retainAll(map2.keySet());
return commonChars.stream()
.map(x -> String.valueOf(x).repeat(Math.min(map1.get(x), map2.get(x))))
.collect(Collectors.joining(""));
}
Test:
System.out.println("common = " + common("adcd", "a05ddd"));
Output:
common = add
Update
As String::repeat
is available starting from Java 11, it may be replaced with the Java 8 compatible code using String::join
+ Collections.nCopies
:
static String common(String w1, String w2) {
if (null == w1 || null == w2) {
return null;
}
Map<Character, Integer> map1 = freqMap(w1);
Map<Character, Integer> map2 = freqMap(w2);
Set<Character> commonChars = new LinkedHashSet<>(map1.keySet());
commonChars.retainAll(map2.keySet());
return commonChars.stream()
.map(x -> String.join("", Collections.nCopies(Math.min(map1.get(x), map2.get(x)), String.valueOf(x))))
.collect(Collectors.joining(""));
}
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("common = " + common("adcd", "a05ddd"));
Output:
Java version: 1.8.0_201
common = add
Update 2
In order to keep the order of the characters in the second string in the result and replace missing characters with '-'
, the following method can be implemented:
static String common2(String w1, String w2) {
if (null == w1 || null == w2) {
return null;
}
Map<Character, Integer> map1 = freqMap(w1);
StringBuilder sb = new StringBuilder();
for (char c : w2.toCharArray()) {
if (map1.containsKey(c) && map1.get(c) > 0) {
sb.append(c);
map1.merge(c, -1, Integer::sum); // decrement the character counter in the first map
} else if (!map1.containsKey(c)) { // character missing in the first word
sb.append('-');
}
}
return sb.toString();
}
Tests
String[][] tests = {
{"adcd", "ad05dd"},
{"adcd", "d05add"},
{"adcd", "d05dadd"},
};
for (String[] test : tests) {
System.out.printf("common(%s, %s) = %s%n", test[0], test[1], common(test[0], test[1]));
}
Output:
common(adcd, ad05dd) = ad--d
common(adcd, d05add) = d--ad
common(adcd, d05dadd) = d--da