简体   繁体   中英

How to solve for sorted anagrams

I have a list of words as follows.

pear amleth dormitory tinsel dirty room hamlet listen silnet

I want to find out all anagrams and list them in sorted order. If nothing is found just output that word. So in the above case the output should be.

amleth,hamlet dirty room,dormitory listen,silnet,tinsel pear

Below is the java code I have written for that.

public class Anagram {

    private  boolean isAnagram(String s1, String s2) {

        if (s1.length() != s2.length()) {
            return false;
        }
        Map<Character, Integer> anagramMap = new HashMap<>();
        for (char ch = 'a'; ch <= 'z'; ++ch) 
             anagramMap.put(ch, 0); 
        for(int i=0; i<s1.length(); i++){
            anagramMap.put(s1.charAt(i), anagramMap.get(s1.charAt(i))+1);
        }
        for(int j=0; j<s2.length(); j++) {
            if (anagramMap.get(s2.charAt(j)) != 0) {
                anagramMap.put(s2.charAt(j), anagramMap.get(s2.charAt(j)) - 1);
            }
        }
        for(int value : anagramMap.values()) {
            if (value != 0) {
                return false;
            }
        }
        return true;
    }
    private void solveChallenge(List<String> words) {
        for(int i=0 ;i<(words.size()-1); i++) {
            Set<String> result = new TreeSet<>();
            for(int j=(i+1); j< words.size(); j++) {
                if (isAnagram(words.get(i), words.get(j))){
                    result.add(words.get(i) + " " + words.get(j));
                    System.out.println(result);
                    words.remove(j);
                }
            }
        }
    }

    public static void main(String[] args) {
        Anagram anagram = new Anagram();
        List<String> words = new ArrayList<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            String line = reader.readLine();
            Integer numTestCases = Integer.parseInt(line);
            while (--numTestCases >= 0){
                words.add(reader.readLine().replaceAll("\\s+","").toLowerCase());
            }
            System.out.println(words);
            new Anagram().solveChallenge(words);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

But it's not listing the desired output. The output I get is [amleth hamlet] [dormitory dirtyroom] [tinsel lisetn]

Can someone tell me what is wrong here?

This will solve your problem

public class Anagram {

private  boolean isAnagram(String s1, String s2) {

    if (s1.length() != s2.length()) {
        return false;
    }
    Map<Character, Integer> anagramMap = new HashMap<>();
    for (char ch = 'a'; ch <= 'z'; ++ch) 
         anagramMap.put(ch, 0); 
    for(int i=0; i<s1.length(); i++){
        anagramMap.put(s1.charAt(i), anagramMap.get(s1.charAt(i))+1);
    }
    for(int j=0; j<s2.length(); j++) {
        if (anagramMap.get(s2.charAt(j)) != 0) {
            anagramMap.put(s2.charAt(j), anagramMap.get(s2.charAt(j)) - 1);
        }
    }
    for(int value : anagramMap.values()) {
        if (value != 0) {
            return false;
        }
    }
    return true;
}
private void solveChallenge(List<String> words) {
    for(int i=0 ;i<(words.size()-1); i++) {
        Set<String> result = new TreeSet<>();
        int j = i+1;
        while(j < words.size()) {
            if (isAnagram(words.get(i), words.get(j))){
                result.add(words.get(i) + " " + words.get(j));
                System.out.println(result);
                words.remove(j);
            } else {
             j++;
           }
        }
    }
}

public static void main(String[] args) {
    Anagram anagram = new Anagram();
    List<String> words = new ArrayList<>();
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    try {
        String line = reader.readLine();
        Integer numTestCases = Integer.parseInt(line);
        while (--numTestCases >= 0){
            words.add(reader.readLine().replaceAll("\\s+","").toLowerCase());
        }
        System.out.println(words);
        new Anagram().solveChallenge(words);
    } catch (IOException e) {
        e.printStackTrace();
    }

}
}

You should increment j only when you are not removing any element because that remove() method caused problems.

Now I would recommend you should use HashMap< String, ArrayList< String>> something like this data structure. Hope this helps :)

This is how I would go about it:

  • for each word, create a Map<Character, Integer> that contains the characters of the word (excluding spaces) and the number of occurence - you have already done something similar
  • then store them in a Map<Map<Character, Integer>, List<String>> , where the key is the map of occurences and the value is a list of all the words that match (anagrams). Map::equals will do the comparison automagically for you.

A sample implementation could look like this:

String[] words = ("pear", "amleth", ... }'
Map<Map<Integer, Long>, List<String>> characters = new HashMap<> ();
for (String word : words) {
  //here I'm using a stream, but you can build the occurences map manually
  Map<Integer, Long> occurences = word.replaceAll("\\s+", "") //remove spaces
                   .chars().boxed()
                   .collect(Collectors.groupingBy(i -> i, Collectors.counting()));

  if (characters.containsKey(occurences)) { //anagram found !
    characters.get(occurences).add(word); //add the word to the list
  } else { //no anagram found, create the list, with only one item
    List<String> list = new ArrayList<> ();
    list.add(word);
    characters.put(occurences, list);
  }
}

//you may want to sort the lists here

characters.values().forEach(System.out::println);

You have made few mistakes in the code. First of all you need to change the logic of storing the anagrams in the set. You are storing here pair in the set rather you should store all anagrams here. So you can have a StringBuilder to store the anagrams in a single pass and after the iteration, You can add it to the set.

Another mistake is in this loop:

for(int j=(i+1); j< words.size(); j++) {
    if (isAnagram(words.get(i), words.get(j))){
        result.add(words.get(i) + " " + words.get(j));
        System.out.println(result);
        words.remove(j);
    }
}

Here you are removing an element from the list and then incrementing j So it is possible that after removal, all elements gets shifted ahead by 1 place so If next element is anagram then you will miss it as you are incrementing j . And last mistake is that you need to check for all elements of words list. Because it may possible that last element is not anagram with any other element, Then it is required to be taken separately.

So Make few changes in solveChallenge() function:

private void solveChallenge(List<String> words) {
    for(int i=0 ;i<(words.size()); i++) {
        Set<String> result = new TreeSet<>();
        StringBuilder resultant_string = new StringBuilder(words.get(i)); //To store the all anagrams
        for(int j=(i+1); j< words.size(); j++) {
            if (isAnagram(words.get(i), words.get(j))){
                resultant_string.append(" ").append(words.get(j));
                words.remove(j);
                j--;       //If anagram found, stay on the current element
            }
        }
        result.add(resultant_string.toString());
        System.out.println(resultant_string);
    }
}

As per your need, I have made some changes in the program.

Code:

class Anagram {

    private  boolean isAnagram(String s1, String s2) {
        s1=s1.replaceAll("\\s+","");
        s2=s2.replaceAll("\\s+","");
        if (s1.length() != s2.length()) {
            return false;
        }
        Map<Character, Integer> anagramMap = new HashMap<>();
        for (char ch = 'a'; ch <= 'z'; ++ch) 
             anagramMap.put(ch, 0); 
        for(int i=0; i<s1.length(); i++){
            anagramMap.put(s1.charAt(i), anagramMap.get(s1.charAt(i))+1);
        }
        for(int j=0; j<s2.length(); j++) {
            if (anagramMap.get(s2.charAt(j)) != 0) {
                anagramMap.put(s2.charAt(j), anagramMap.get(s2.charAt(j)) - 1);
            }
        }
        for(int value : anagramMap.values()) {
            if (value != 0) {
                return false;
            }
        }
        return true;
    }
    private void solveChallenge(List<String> words) {
        List<String> result = new ArrayList<>();
        for(int i=0 ;i<(words.size()); i++) {
            List<String> resultant_strings=new ArrayList<> ();
            resultant_strings.add(words.get(i));

            for(int j=(i+1); j< words.size(); j++) {
                if (isAnagram(words.get(i), words.get(j))){
                    resultant_strings.add(words.get(j));
                    words.remove(j);
                    j--;
                }
            }
            Collections.sort(resultant_strings);
            String resultant_string=resultant_strings.toString();
            result.add(resultant_string);
        }
        Collections.sort(result);
        System.out.println(result);
    }

    public static void main(String[] args) {
        Anagram anagram = new Anagram();
        List<String> words = new ArrayList<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            String line = reader.readLine();
            Integer numTestCases = Integer.parseInt(line);
            while (--numTestCases >= 0){
                words.add(reader.readLine().toLowerCase());
            }
            System.out.println(words);
            new Anagram().solveChallenge(words);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

Prints result:

[[amleth, hamlet], [dirty room, dormitory], [listen, silnet, tinsel], [pear]]

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