简体   繁体   中英

Getting the common character count between two strings

Given two strings, find the number of common characters between them.

Example

For s1 = "aabcc" and s2 = "adcaa", the output should be commonCharacterCount(s1, s2) = 3.

Strings have 3 common characters - 2 "a"s and 1 "c".

I've been stuck on this problem for quite a bit and I've tried many approaches to solving this problem, but I cannot quite figure it out. I have the principal idea of how to solve it, but I cannot transfer that to code.

What my approach is to put the string's characters in their own ArrayList and use nested loops to iterate through them comparing the similar characters and storing the count in a int value. I don't have any code because I have tried many different attempts varying my code, but no luck.

I used two nested for loops that are separate from each other like this:

for(int i = 0; i<firstString.size(); i++){
    for(int j = 0; j<secondString.size(); j++){
        if(firstString.get(i) == secondString.get(j){
            lettersInCommon++;
            secondString.remove(j);
            }
        }
}

And

for(int i = 0; i<secondString.size(); i++){
    for(int j = 0; j<firstString.size(); j++){
        if(firstString.get(i) == secondString.get(j){
            lettersInCommon2++;
            firstString.remove(i);
            }
        }
}

So after these loops run, I return the difference between the two lettersinCommon ints depending on their size to avoid negatives. So if lettersInCommon > lettersInCommon2 -- return lettersInCommon - lettersInCommon2; and vice versa.

I don't want anyone to tell me how to code this, I would only like advice towards my logic to see if I can simplify this problem or if I am missing something.

I would also like to state that this code works for some test cases but not all.

I've been taking into account the comments I've received and gotten this far:

ArrayList<Character> firstString = new ArrayList<Character>();
ArrayList<Character> secondString = new ArrayList<Character>();
int lettersInCommon = 0;

for(int i = 0; i<s1.length(); i++){
    firstString.add(s1.charAt(i));
}

for(int i = 0; i<s2.length(); i++){
    secondString.add(s2.charAt(i));
}
Collections.sort(firstString);
Collections.sort(secondString);
for(char c : firstString){
    if(firstString.contains(c) && secondString.contains(c)){
        lettersInCommon++;
        secondString.remove(s2.indexOf(c));
    }
}

I am really close, but the error I get is an out of bounds exception on this line secondString.remove(s2.indexOf(c));

Does anyone have any insight on this?

You could go for maps. It may not be the best solution in terms of performance, but imo one that is intuitive to understand. First, iterate each string and collect each of its (distinct) characters with their appearance count. Then, compare the keysets of both maps (ie the characters) and for each character you find in both maps, store it together with its minimum appearance count from both maps. So something like this:

// first collect characters with their appearance count in maps:
"aabcc" -> 2xa, 1xb, 2xc
"adcaa" -> 3xa, 1xc, 1xd

// now, get all shared characters with their minimum count from both maps:
a -> min(2,3) = 2
b -> not shared
c -> min(2,1) = 1
d -> not shared

I guess this could be implemented in a cool way using the Stream API, but it would be quite a complex statement, not sure whether you have experience with Streams.

edit: Here's one solution using Streams. I bet there are better ones, both performance-wise and from its approach, but it's the first thing that I tried:

public static void main(String[] args) {
    System.out.println(commonCharacterCount("aabcc","adcaa"));
}

public static int commonCharacterCount(String s1, String s2) {
    Map<Character, Integer> s1CharacterCount = getCharacterCount(s1);
    Map<Character, Integer> s2CharacterCount = getCharacterCount(s2);
    return s1CharacterCount.keySet().stream()
            .filter(s2CharacterCount.keySet()::contains)
            .mapToInt(c -> Math.min(s1CharacterCount.get(c), s2CharacterCount.get(c)))
            .sum();
}

public static Map<Character, Integer> getCharacterCount(String s) {
    Map<Character, Integer> characterCount = new HashMap<>();
    for (char c: s.toCharArray()) {
        characterCount.put(c, characterCount.computeIfAbsent(c, count -> 0) + 1);
    }
    return characterCount;
}
  1. Don't compare objects with ==
  2. Don't reinvent the wheel: you don't need to loop through a list to know if it contains an element. There's contains() to do that for you.
  3. You don't even need contains() , since remove() tells you if something has been removed or not. So all you need is:

    1. Create a List<Character> from string2
    2. Loop through string1's characters, or until the list is empty
    3. At each iteration, remove the current character from the list. If it was removed, increment the count

Use the s1.charAt(i) == s2.charAt(i) comparison. There is no need to put the strings into an arrayList.

Edit: You'll need to add a conditional statement to make sure that you don't go out of bounds when the strings are of different lengths.

Create a map of the letter counts for each string you give it. Then compare the keys to one another and if the letter is contained in both words, grab the lowest count from the two maps. Try this

public static void main(String[] args)
{
    String str1 = "aabbcc";
    String str2 = "abcddc";
    HashMap<Character, Integer> str1LetterCounts = createLetterCountMap(str1);
    HashMap<Character, Integer> str2LetterCounts = createLetterCountMap(str2);

    HashMap<Character, Integer> commonCounts = new HashMap<>();

    Set<Character> possibleCommonCharacterList = str1LetterCounts.keySet();

    for(Character letter : possibleCommonCharacterList)
    {
        if(str2LetterCounts.containsKey(letter))
        {
            Integer count1 = str1LetterCounts.get(letter);
            Integer count2 = str2LetterCounts.get(letter);
            commonCounts.put(letter, count1 <= count2 ? count1 : count2);
            System.out.println("Common Character " + letter + " : " + (count1 <= count2 ? count1 : count2));
        }
    }

}

public static HashMap<Character, Integer> createLetterCountMap(String word)
{
    HashMap<Character, Integer> letterCountsForWord = new HashMap<>();
    for(int i = 0; i < word.length(); i++)
    {
        Character key = word.charAt(i);
        if(letterCountsForWord.containsKey(key))
        {
            letterCountsForWord.put(key, letterCountsForWord.get(key) + 1);
        }
        else
            letterCountsForWord.put(key, 1);
    }

    return letterCountsForWord;
}

Output

Common Character a : 1
Common Character b : 1
Common Character c : 2
int solution(String s1, String s2) {
    int count = 0;
    String[] arr1 = s1.split("");
    String[] arr2 = s2.split("");
    for (int i = 0; i < s1.length(); i++) {
        String s = arr1[i];//anymatch operator complaining if I directly give arr[i]
        if (Arrays.stream(arr2).anyMatch(t -> t.equals(s))) {
            for (int j = 0; j < arr2.length; j++) {
                if (arr2[j].equals(arr1[i])) {
                    arr2[j] = "";
                    break;
                }
            }
            arr1[i] = "";
            count++;
        }

    }

    return count;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM