Search code examples
javacontainshashset

Hashset contains with exact character placement and incorrect character placement


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 + "-";
    }

Solution

  • 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(""));
        }
    

    Online demo:

    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