简体   繁体   中英

How can I make anagram program code faster? (java)

I am a not so experienced programmer. I've written an anagram program and the only problem is that its not fast enough. I've been told that my nested for loop is the problem, but i can't figure how to fix it ( set1 is a HashSet with all the words I'm working on, map is a LinkedHashMap<String, String> and anagram a TreeMap<String, TreeSet<String>> ):

for (String element : set1) {
    char[] woord = element.toCharArray(); //alfabetical order of each word
    Arrays.sort(woord);
    String chartostring = new String(woord);
    map.put(element, chartostring); // add each word with its sorted letters
    TreeSet<String> order_words = new TreeSet<String>(); //creating list of anagrams
    for (String o : map.keySet()) { //for each word
        if (map.get(o).equals(chartostring)) { //check if there is a value in map which is equal to the sorted letters
            order_words.add(o); //add word to list of anagrams
            if (order_words.size() > 1) { //we want anagrams so only print if there are atleast 2 words
                anagram.put(chartostring, order_words);
            }
        }
    }
}

Can someone please help me out? Thanks a lot.

The nested loop is, indeed, slow, because you iterate a map as if it were a list. Wouldn't it be nice if you could replace the nested loop with a quick look-up in a hash map? Unfortunately, this is not an option when you deal with Map<String,String> , because multiple words will have identical sorted representation. That is why you built a map from a word to its sorted representation, not the other way around.

What this means, however, is that you can build a map from a sorted representation to a list of words:

Map<String,List<String>> sortedRepToWords

You can build this map in a single loop, before doing any matching. With this map-of-lists in place, you can eliminate the nested loop, replacing it with a look-up of the entire list from sortedRepToWords .

Introducing Java 8...

import java.util.stream.Stream;
import static java.util.stream.Collectors.*;

Map<String, List<String>> tmp = set1.parallelStream() // Possibly using multiple cores...
    .collect(groupingBy(s-> sortMe(s))); // One liner!

List<List<String>> result = tmp.values().stream()
    .filter(l -> l.size() > 1)  // At least 2 anagrams
    .collect(toList());

System.out.println(tmp);
System.out.println(result);

//...//

// If you really want to use Java 8 for sorting a String...
private String sortMe(String input) {
    return Stream.of(input.split("")).sorted().collect(joining());
}

you can do this in order of 1 (single loop) if you use the HashSet.

Set<char[]> dict_set = new HashSet<char[]>();
Set<char[]> anag_set = new HashSet<char[]>();

for (String element : set1) {

 // Sort the characters in string.
 char[] woord= element.toCharArray();
 Arrays.sort(woord);

 //if already encountered add as a known anagram
 //else add the sorted charset to the dictionary.

 if (dict_set.contains(woord)) anag_set.add(woord)
 else dict_set.add(word);

 return anag_set;
}

Incase you need all the strings that are anagrams and its sortedform; you can use a map of sorted-form and a list of all associated anagrams.

PS : its just a psuedocode.

I dont know if this is better in your case, but when I need key and value of a map I use the entrySet . So the call of the get method is avoied and a part of time is saved ...

https://stackoverflow.com/a/3870210/1811730

for (String element : set1) {
    char[] woord = element.toCharArray(); //alfabetical order of each word
    Arrays.sort(woord);
    String chartostring = new String(woord);
    map.put(element, chartostring); // add each word with its sorted letters
    TreeSet<String> order_words = new TreeSet<String>(); //creating list of anagrams
    for (Entry o : map.entrySet()) { //for each word
        if (o.getValue().equals(chartostring)) { //check if there is a value in map which is equal to the sorted letters
            order_words.add(o.getKey()); //add word to list of anagrams
            if(order_words.size()>1){ //we want anagrams so only print if there are atleast 2 words
                  anagram.put(chartostring, order_words); 
              }
        }
    }
}

If it is not enough, I think you should rewrite your code with another logic and try to implement the answer of dasblinkenlight

// Importing the Arrays class that will help us manipulate arrays.
import java.util.Arrays;

public class AnagramTest {

  private static boolean isAnagram(String str1 , String str2){
    // Converting both strings to char arrays as strings do not have direct
    // sorting method in java.
    char [] leftArray = ( str1.trim().toLowerCase()).toCharArray();
    char [] rightArray = ( str2.trim().toLowerCase()).toCharArray();
    Arrays.parallelSort(leftArray);
    Arrays.parallelSort(rightArray);

    if(leftArray.length != rightArray.length){
      return false;
    }
    // Both char arrays have the same number of characters
    for(int i = 0; i < leftArray.length; i++){
      if(leftArray[i] != rightArray[i]){
        return false;  
      }
    }
    // We only get to this stage if all the elements pass the if test
    return true;
  }

  public static void main(String [] args){
    String a = "integral"; // initializing first string
    String b = "Triangle"; // initializing second string
    System.out.println(isAnagram(a,b));     
  }         
}

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