简体   繁体   中英

Why the following could determine if str1 and str2 are anagram?

I am reading a code snippet and I am wondering why the following could check if str1 and str2 are anagram.

boolean result =  str1.chars().allMatch( c1 -> str2.chars()
                   .anyMatch( c2 -> c1 == c2) );

I am particularly confused about if str1 = "abc" str2 = "cbad", the result should be also true, right?

I am particularly confused about if str1 = "abc" str2 = "cbad", the result should be also true, right?

The code will return true , yes. So no, it's not a valid check for an anagram, since in the example you cite, it will say it's an anagram when it's not (since str1 doesn't have d ).

In an anagram, both strings would need to have the same number of occurrences of the same letters. One byproduct of that is that they would need to be the same length (although in some anagram schemes, you're allowed to vary the number of spaces and add/remove punctuation).

So a valid check would need to ensure that every character in str1 (perhaps ignoring spaces and punctuation) consumed a character from str2 , and that there were no characters from str2 left at the end (perhaps ignoring spaces and punctuation). There are lots of ways to do that (count the occurrences in str1 , perhaps in a Map , and then count down matches from str2 ; have a string you remove letters from, etc.) The quoted code just isn't one of them.

If you have str1 = "abc" and str2 = "cbad", they cannot be anagram of each other because str2 contains the extra character d which str1 does not have. For one word to be an anagram of the other word, they each needs to have the same character with the same number of frequencies.

That being said, I just ran the code you have written and it does not work for that specific case that you have mentioned.

One way you can determine if one string is an anagram of another string is by using two HashMap. For each strings, loop through it, track each character as the key and its frequencies as value. At the end, you can loop through both of the HashMap and keep comparing the value for each key. If the values are not equal, return false.

Also first thing to check is that the length of both strings should be equal. If they fail that condition, they cannot be an anagram then.

The following is the code I wrote on LeetCode many months ago. This can definitely be optimized and made much more cleaner but just to give you an idea.

public class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        HashMap<Character, Integer> tmap = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (map.containsKey(c)) {
                int count = map.get(c);
                map.put(c, count + 1);
            }
            else {
                map.put(c, 1);
            }
        }
        for (int j = 0; j < t.length(); j++) {
            char d = t.charAt(j);
            if (!(map.containsKey(d))) {
                return false;
            }
            if (tmap.containsKey(d)) {
                int tracker = tmap.get(d);
                tmap.put(d, tracker + 1);
            }
            else {
                tmap.put(d, 1);
            }
        }
        for (Character key: map.keySet()) {
            int mapv = map.get(key);
            int tmapv = tmap.get(key);
            if (mapv != tmapv) {
                System.out.println(tmapv);
                System.out.println(mapv);
                return false;
            }
        } 
        return true;
    }
}

Based on TJ Crowder's answer this should be a possible way to check for an anagram:

public static boolean isAnagram(String subject, String possibleAnagram, IntPredicate isAnagramCodePoint)
{
    Objects.requireNonNull(subject);
    Objects.requireNonNull(possibleAnagram);
    Objects.requireNonNull(isAnagramCodePoint);

    // Convert inputs to valid anagram letters.
    int[] subjectLetters = subject.codePoints()
            .filter(isAnagramCodePoint)
            .toArray();

    int[] possibleAnagramLetters = possibleAnagram.codePoints()
            .filter(isAnagramCodePoint)
            .toArray();

    // Permutation = Same number of occurrences of the same letter.

    if (subjectLetters.length != possibleAnagramLetters.length)
    {
        return false;
    }

    HashMap<Integer, Integer> occurrences = new HashMap<>(subjectLetters.length);

    // Count (produce) number of occurrences for each letter in subject.
    for (int codePoint : subjectLetters)
    {
        occurrences.merge(codePoint, 1, (a, b) -> a + b);
    }

    // Consume each letter occurrence from subjectLetters.
    for (int codePoint : possibleAnagramLetters)
    {
        int val = occurrences.merge(codePoint, -1, (a, b) -> a + b);

        if (val == -1)
        {
            return false; // Current letter occurred more often in possibleAnagram
        }
    }

    // Check equal number of occurrences for the same letter.
    for (int number : occurrences.values())
    {
        if (number != 0)
        {
            return false;
        }
    }

    return true;
}

private static final IntPredicate COMPLETE = (codePoint) -> true;

public static boolean isAnagram(String subject, String possibleAnagram)
{
    return isAnagram(subject, possibleAnagram, COMPLETE);
}

The optional IntPredicate is used to specify the anagram schema, a possible value could be Character::isLetter to ignore all code points except letters, Character::isDigit , etc.

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