java string permutations and combinations lookup - java

I'm writing an Android word app. My code includes a method that would find all combinations of the string and the substrings of a 7 letter string with a minimum of length 3. Then compare all available combination to every word in the dictionary to find all the valid words. I'm using a recursive method. Here's the code.
// Gets all the permutations of a string.
void permuteString(String beginningString, String endingString) {
if (endingString.length() <= 1){
if((Arrays.binarySearch(mDictionary, beginningString.toLowerCase() + endingString.toLowerCase())) >= 0){
mWordSet.add(beginningString + endingString);
}
}
else
for (int i = 0; i < endingString.length(); i++) {
String newString = endingString.substring(0, i) + endingString.substring(i + 1);
permuteString(beginningString + endingString.charAt(i), newString);
}
}
// Get the combinations of the sub-strings. Minimum 3 letter combinations
void subStrings(String s){
String newString = "";
if(s.length() > 3){
for(int x = 0; x < s.length(); x++){
newString = removeCharAt(x, s);
permuteString("", newString);
subStrings(newString);
}
}
}
The above code runs fine but when I installed it on my Nexus s I realized that it runs a bit too slow. It takes a few seconds to complete. About 3 or 4 seconds which is unacceptable.
Now I've played some word games on my phone and they compute all the combinations of a string instantly which makes me believe that my algorithm is not very efficient and it can be improved. Can anyone help?
public class TrieNode {
TrieNode a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
TrieNode[] children = {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z};
private ArrayList<String> words = new ArrayList<String>();
public void addWord(String word){
words.add(word);
}
public ArrayList<String> getWords(){
return words;
}
}
public class Trie {
static String myWord;
static String myLetters = "afinnrty";
static char[] myChars;
static Sort sort;
static TrieNode myNode = new TrieNode();
static TrieNode currentNode;
static int y = 0;
static ArrayList<String> availableWords = new ArrayList<String>();
public static void main(String[] args) {
readWords();
getPermutations();
}
public static void getPermutations(){
currentNode = myNode;
for(int x = 0; x < myLetters.length(); x++){
if(currentNode.children[myLetters.charAt(x) - 'a'] != null){
//availableWords.addAll(currentNode.getWords());
currentNode = currentNode.children[myLetters.charAt(x) - 'a'];
System.out.println(currentNode.getWords() + "" + myLetters.charAt(x));
}
}
//System.out.println(availableWords);
}
public static void readWords(){
try {
BufferedReader in = new BufferedReader(new FileReader("c://scrabbledictionary.txt"));
String str;
while ((str = in.readLine()) != null) {
myWord = str;
myChars = str.toCharArray();
sort = new Sort(myChars);
insert(myNode, myChars, 0);
}
in.close();
} catch (IOException e) {
}
}
public static void insert(TrieNode node, char[] myChars, int x){
if(x >= myChars.length){
node.addWord(myWord);
//System.out.println(node.getWords()+""+y);
y++;
return;
}
if(node.children[myChars[x]-'a'] == null){
insert(node.children[myChars[x]-'a'] = new TrieNode(), myChars, x=x+1);
}else{
insert(node.children[myChars[x]-'a'], myChars, x=x+1);
}
}
}

In your current approach, you're looking up every permutation of each substring. So for "abc", you need to look up "abc", "acb", "bac", "bca", "cab" and "cba". If you wanted to find all permutations of "permutations", your number of lookups is nearly 500,000,000, and that's before you've even looked at its substrings. But we can reduce this to one lookup, regardless of length, by preprocessing the dictionary.
The idea is to put each word in the dictionary into some data structure where each element contains a set of characters, and a list of all words containing (only) those characters. So for example, you could build a binary tree, which would have a node containing the (sorted) character set "abd" and the word list ["bad", "dab"]. Now, if we want to find all permutations of "dba", we sort it to give "abd" and look it up in the tree to retrieve the list.
As Baumann pointed out, tries are well suited to storing this kind of data. The beauty of the trie is that the lookup time depends only on the length of your search string - it is independent of the size of your dictionary. Since you'll be storing quite a lot of words, and most of your search strings will be tiny (the majority will be the 3-character substrings from the lowest level of your recursion), this structure is ideal.
In this case, the paths down your trie would reflect the character sets rather than the words themselves. So if your entire dictionary was ["bad", "dab", "cab", "cable"], your lookup structure would end up looking like this:
There's a bit of a time/space trade-off in the way you implement this. In the simplest (and fastest) approach, each Node contains just the list of words, and an array Node[26] of children. This allows you to locate the child you're after in constant time, just by looking at children[s.charAt(i)-'a'] (where s is your search string and i is your current depth in the trie).
The downside is that most of your children arrays will be mostly empty. If space is an issue, you can use a more compact representation like a linked list, dynamic array, hash table, etc. However, these come at the cost of potentially requiring several memory accesses and comparisons at each node, instead of the simple array access above. But I'd be surprised if the wasted space was more than a few megabytes over your whole dictionary, so the array-based approach is likely your best bet.
With the trie in place, your whole permutation function is replaced with one lookup, bringing the complexity down from O(N! log D) (where D is the size of your dictionary, N the size of your string) to O(N log N) (since you need to sort the characters; the lookup itself is O(N)).
EDIT: I've thrown together an (untested) implementation of this structure: http://pastebin.com/Qfu93E80

See here: How to find list of possible words from a letter matrix [Boggle Solver]
The idea behind the code in the answers is as follows:
Iterate over each word dictionary.
Iterate over each letter in the word, adding it to a string and adding the string each time to an array of prefixes.
When creating string combinations, test to see that they exist in the prefix array before branching any further.

static List<String> permutations(String a) {
List<String> result=new LinkedList<String>();
int len = a.length();
if (len<=1){
result.add(a);
}else{
for (int i=0;i<len; i++){
for (String it:permutations(a.substring(0, i)+a.substring(i+1))){
result.add(a.charAt(i)+it);
}
}
}
return result;
}

I don't think adding all permutations is necessary. You can simply encapsulate the string into a PermutationString:
public class PermutationString {
private final String innerString;
public PermutationString(String innerString) {
this.innerString = innerString;
}
#Override
public int hashCode() {
int hash = 0x00;
String s1 = this.innerString;
for(int i = 0; i < s1.length(); i++) {
hash += s1.charAt(i);
}
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PermutationString other = (PermutationString) obj;
int nChars = 26;
int[] chars = new int[nChars];
String s1 = this.innerString;
String s2 = other.innerString;
if(s1.length() != s2.length()) {
return false;
}
for(int i = 0; i < s1.length(); i++) {
chars[s1.charAt(i)-'a']++;
}
for(int i = 0; i < s2.length(); i++) {
chars[s2.charAt(i)-'a']--;
}
for(int i = 0; i < nChars; i++) {
if(chars[i] != 0x00) {
return false;
}
}
return true;
}
}
A PermutationString is a string, but where two PermutationStrings are equal if they have the same frequency of characters. Thus new PermutationString("bad").equals(new PermutationString("dab")). This also holds for the .hashCode(): if the strings are permutations of each other, they will generate the same .hashCode().
Now you can simply a HashMap<PermutationString,ArrayList<String>> as follows:
HashMap<PermutationString,ArrayList<String>> hm = new HashMap<PermutationString,ArrayList<String>>();
String[] dictionary = new String[] {"foo","bar","oof"};
ArrayList<String> items;
for(String s : dictionary) {
PermutationString ps = new PermutationString(s);
if(hm.containsKey(ps)) {
items = hm.get(ps);
items.add(s);
} else {
items = new ArrayList<String>();
items.add(s);
hm.put(ps,items);
}
}
So now we iterate over all possible words in the dictionary, construct a PermutationString as key, and if the key already exists (that means that there is already a word with the same character frequencies), we simply add our own word to it. Otherwise, we add a new ArrayList<String> with the single word.
Now that we have filled up the hm with all permutations (but not that much keys), you can query:
hm.get(new PermutationString("ofo"));
This will return an ArrayList<String> with "foo" and "oof".
Testcase:
HashMap<PermutationString, ArrayList<String>> hm = new HashMap<PermutationString, ArrayList<String>>();
String[] dictionary = new String[]{"foo", "bar", "oof"};
ArrayList<String> items;
for (String s : dictionary) {
PermutationString ps = new PermutationString(s);
if (hm.containsKey(ps)) {
items = hm.get(ps);
items.add(s);
} else {
items = new ArrayList<String>();
items.add(s);
hm.put(ps, items);
}
}
Assert.assertNull(hm.get(new PermutationString("baa")));
Assert.assertNull(hm.get(new PermutationString("brr")));
Assert.assertNotNull(hm.get(new PermutationString("bar")));
Assert.assertEquals(1,hm.get(new PermutationString("bar")).size());
Assert.assertNotNull(hm.get(new PermutationString("rab")));
Assert.assertEquals(1,hm.get(new PermutationString("rab")).size());
Assert.assertNotNull(hm.get(new PermutationString("foo")));
Assert.assertEquals(2,hm.get(new PermutationString("foo")).size());
Assert.assertNotNull(hm.get(new PermutationString("ofo")));
Assert.assertEquals(2,hm.get(new PermutationString("ofo")).size());
Assert.assertNotNull(hm.get(new PermutationString("oof")));
Assert.assertEquals(2,hm.get(new PermutationString("oof")).size());

Use a Trie
Instead of testing all N! possibilities, you only follow prefix trees that lead to a result. This will significanlty reduce the amount of strings that you're checking against.

Well, you can extend your dictionary entities with array letters[] where letters[i] stays for times that i-th letter of alphabet used in this word. It'll take some additional memory, not far much than it is used now.
Then, for each word which permutations you want to check, you'll need to count number of distinct letters too and then traverse through dictiory with easy comparison procedure. If for all letters for word from dictionary number of occurrences less or equal than for word we are checking - yes, this word can be represented as permutation of substring, otherwise - no.
Complexity: it'll took O(D * maxLen) for precalculation, and O(max(N, D)) for each query.

Related

Why the hashset's performance is way faster than list?

This problem is from leetcode (https://leetcode.com/problems/word-ladder/)!
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
Only one letter can be changed at a time.
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
This is my code which takes 800 ms to run:
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList){
if(!wordList.contains(endWord))
return 0;
int ret = 1;
LinkedList<String> queue = new LinkedList<>();
Set<String> visited = new HashSet<String>();
queue.offer(beginWord);
queue.offer(null);
while(queue.size() != 1 && !queue.isEmpty()) {
String temp = queue.poll();
if(temp == null){
ret++;
queue.offer(null);
continue;
}
if(temp.equals(endWord)) {
//System.out.println("succ ret = " + ret);
return ret;
}
for(String word:wordList) {
if(diffOf(temp,word) == 1){
//System.out.println("offered " + word);
//System.out.println("ret =" + ret);
if(!visited.contains(word)){
visited.add(word);
queue.offer(word);
}
}
}
}
return 0;
}
private int diffOf(String s1, String s2) {
if(s1.length() != s2.length())
return Integer.MAX_VALUE;
int dif = 0;
for(int i=0;i < s1.length();i++) {
if(s1.charAt(i) != s2.charAt(i))
dif++;
}
return dif;
}
}
Here is another code which takes 100ms to run:
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> set = new HashSet<>(wordList);
if (!set.contains(endWord)) {
return 0;
}
int distance = 1;
Set<String> current = new HashSet<>();
current.add(beginWord);
while (!current.contains(endWord)) {
Set<String> next = new HashSet<>();
for (String str : current) {
for (int i = 0; i < str.length(); i++) {
char[] chars = str.toCharArray();
for (char c = 'a'; c <= 'z'; c++) {
chars[i] = c;
String s = new String(chars);
if (s.equals(endWord)) {
return distance + 1;
}
if (set.contains(s)) {
next.add(s);
set.remove(s);
}
}
}
}
distance++;
if (next.size() == 0) {
return 0;
}
current = next;
}
return 0;
}
}
I think the second code is less efficient, because it test 26 letters for each word. Why is it so fast?
Short answer: Your breath-first search does orders of magnitude more compares per 'word distance unit' (hereafter called iteration).
You compare every candidate to every remaining word. Time complexity T(N×n) per iteration,
They compare every candidate to artificially constructed 'next' candidates. And because they construct candidates they don't have to 'calculate' the distance. For simplicity, I assume both (constructing or checking) have the same running time. The time complexity is T(26×l×n) per iteration.
(N=word list size, n = number of candidates for this iteration, l = word length)
Of course 26×l×n is much less than N×n because the word length is small but the word list is huge.
I tried your routine on ("and","has",[List of 2M English words]) and after 30 seconds I killed it because I thought it crashed. It didn't crash, it was just slow. I turned to another word list of 50K and yours now takes 8 seconds, vs 0.04s for their implementation.
For my word list of N=51306 there are 2167 3-letter words. This means that for every word, on average, there are 3×cbrt(2167) possible candidates, which is n≈38.82.
Their expected performance: T(26×l×n) ≈ T(3027) work per iteration,
Your expected performance: T(N×n) ≈ T(1991784) work per iteration.
(assuming word list does not get shorter; but with this many words the difference is negligible)
Incidentally, your queue-based circular buffer implementation is possibly faster than their two-alternating-Sets implementation, so you could make a hybrid that's even faster.

How can I check if two strings are permutations of each other in O(n) time? (java)

I wrote this class that can check if two given strings are permutations of each other. However, it is my understanding that this runs at O(n^2) time because the string.indexOf() runs at O(n) time.
How can this program be made more efficient?
import java.util.*;
public class IsPermutation{
public void IsPermutation(){
System.out.println("Checks if two strings are permutations of each other.");
System.out.println("Call the check() method");
}
public boolean check(){
Scanner console = new Scanner(System.in);
System.out.print("Insert first string: ");
String first = console.nextLine();
System.out.print("Insert second string: ");
String second = console.nextLine();
if (first.length() != second.length()){
System.out.println("Not permutations");
return false;
}
for (int i = 0; i < first.length(); i++){
if (second.indexOf(first.charAt(i)) == -1){
System.out.println("Not permutations");
return false;
}
}
System.out.println("permutations");
return true;
}
}
First, it can be done in O(nlogn) by sorting the two strings (after converting them to char[]), and then simple equality test will tell you if the original strings are permutations or not.
An O(n) solution average case can be achieved by creating a HashMap<Character, Integer>, where each key is a character in the string, and the value is the number of its occurances (This is called a Histogram). After you have it, again a simple equality check of the two maps will tell you if the original strings are permutations.
One way to archive O(n) is to count the frequency of every character.
I would use a HashMap with the characters as keys and the frequencys as values.
//create a HashMap containing the frequencys of every character of the String (runtime O(n) )
public HashMap<Character, Integer> getFrequencys(String s){
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0; i<s.length(); i++){
//get character at position i
char c = s.charAt(i);
//get old frequency (edited: if the character is added for the
//first time, the old frequency is 0)
int frequency;
if(map.containsKey(c)){
frequency = map.get(c);
}else{
frequency = 0;
}
//increment frequency by 1
map.put(c, frequency+1 );
}
return map;
}
now you can create a HashMap for both Strings and compare if the frequency of every character is the same
//runtime O(3*n) = O(n)
public boolean compare(String s1, String s2){
if(s1.length() != s2.length()){
return false;
}
//runtime O(n)
HashMap<Character, Integer> map1 = getFrequencys(s1);
HashMap<Character, Integer> map2 = getFrequencys(s2);
//Iterate over every character in map1 (every character contained in s1) (runtime O(n) )
for(Character c : map1.keySet()){
//if the characters frequencys are different, the strings arent permutations
if( map2.get(c) != map1.get(c)){
return false;
}
}
//since every character in s1 has the same frequency in s2,
//and the number of characters is equal => s2 must be a permutation of s1
return true;
}
edit: there was a nullpointer error in the (untested) code
Sorting Solution:
public void IsPermutation(String str1, String str2) {
char[] sortedCharArray1 = Arrays.sort(str1.toCharArray());
char[] sortedCharArray2 = Arrays.sort(str2.toCharArray());
return Arrays.equals(sortedCharArray1, sortedCharArray2);
}
Time Complexity: O(n log n)
Space Complexity: O(n)
Frequency count solution:
//Assuming that characters are only ASCII. The solutions can easily be modified for all characters
public void IsPermutation(String str1, String str2) {
if (str1.length() != str2.length())
return false;
int freqCountStr1[] = new int[256];
int freqCountStr2[] = new int[256];
for (int i = 0; i < str1.length(); ++i) {
int c1 = str1.charAt(i);
int c2 = str2.charAt(i);
++freqCountStr1[c1];
++freqCountStr2[c2];
}
for (int i = 0; i < str1.length(); ++i) {
if (freqCountStr1[i] != freqCountStr2[i]) {
return false;
}
}
return true;
}
}
Time Complexity: O(n)
Space Complexity: O(256)

Boolean algorithm producing true output when output should be false

Basically i am trying to create an algorithm that will test whether a given string is a cover string for a list of strings. A string is a cover string if it contains the characters for every string in the list in a way that maintains the left to right order of the listed strings. For example, for the two strings "cat" and "dog", "cadhpotg" would be a cover string, but "ctadhpog" would not be one.
I have created an algorithm however it is producing the output true when the output should be false, as the given string is a cover String for Strings list1 and list2, but not for list3.
Any help into why this algorithm is producing the wrong output would be highly appreciated.
public class StringProcessing2 {
//ArrayList created and 3 fields added.
public static ArrayList<String> stringList = new ArrayList<>();
public static String list1 = "phillip";
public static String list2 = "micky";
public static String list3 = "fad";
//Algorithm to check whether given String is a cover string.
public static boolean isCover(String coverString){
int matchedWords = 0;
stringList.add(list1);
stringList.add(list2);
stringList.add(list3);
//for-loops to iterate through each character of every word in stringList to test whether they appear in
//coverString in left to right order.
for(int i = 0; i < stringList.size(); i++){
int countLetters = 1;
for(int n = 0; n < (stringList.get(i).length())-1; n++){
if(coverString.indexOf(stringList.get(i).charAt(n)) <= (coverString.indexOf((stringList.get(i).charAt(n+1)),
coverString.indexOf((stringList.get(i).charAt(n)))))){
countLetters++;
if(countLetters == stringList.get(i).length()){
matchedWords++;
}
}
}
}
if(matchedWords == stringList.size()){
return true;
}
else
return false;
}
public static void main(String[] args) {
System.out.println(isCover("phillmickyp"));
}
}
Probably the easiest way to go about this is to break down the problem into parts. Have every function do the least possible work while still getting something done towards the overall goal.
To accomplish this, I'd recommend creating a helper method that takes two Strings and returns a boolean, checking if one String is the cover of another.
boolean isCover(String s, String cover)
{
int i = 0;
for(char c : s.toCharArray())
if((i = cover.indexOf(c, i)) == -1)
return false;
return true;
}
Then once you have something that can correctly tell you if it's a valid cover String or not, it becomes much simpler to check if one String is a valid cover for multiple Strings
boolean isCover(List<String> strings, String cover)
{
for(String s : strings)
if(!isCover(s, cover))
return false;
return true;
}

Finding Anagrams with vowels always at the end

I was trying out this question :
Write a function using Recursion to display all anagrams of a string entered by the user, in such a way that all its vowels are located at the end of every anagram. (E.g.: Recursion => Rcrsneuio, cRsnroieu, etc.) Optimize it.
From this site :
http://erwnerve.tripod.com/prog/recursion/magic.htm
This is what i have done :
public static void permute(char[] pre,char[] suff) {
if (isEmpty(suff)) {
//result is a set of string. toString() method will return String representation of the array.
result.add(toString(moveVowelstoEnd(pre)));
return;
}
int sufflen = getLength(suff); //gets the length of the array
for(int i =0;i<sufflen;i++) {
char[] tempPre = pre.clone();
char[] tempSuf = suff.clone();
int nextindex = getNextIndex(pre); //find the next empty spot in the prefix array
tempPre[nextindex] = tempSuf[i];
tempSuf = removeElement(i,tempSuf); //removes the element at i and shifts array to the left
permute(tempPre,tempSuf);
}
}
public static char[] moveVowelstoEnd(char[] input) {
int c = 0;
for(int i =0;i<input.length;i++) {
if(c>=input.length)
break;
char ch = input[i];
if (vowels.contains(ch+"")) {
c++;
int j = i;
for(;j<input.length-1;j++)
input[j] = input[j+1];
input[j]=ch;
i--;
}
}
return input;
}
Last part of the question is 'Optimize it'. I am not sure how to optimize this. can any one help?
Group all the vowels into v
Group all consonants into w
For every pair of anagrams, concat the results

How to check if two words are anagrams

I have a program that shows you whether two words are anagrams of one another. There are a few examples that will not work properly and I would appreciate any help, although if it were not advanced that would be great, as I am a 1st year programmer. "schoolmaster" and "theclassroom" are anagrams of one another, however when I change "theclassroom" to "theclafsroom" it still says they are anagrams, what am I doing wrong?
import java.util.ArrayList;
public class AnagramCheck {
public static void main(String args[]) {
String phrase1 = "tbeclassroom";
phrase1 = (phrase1.toLowerCase()).trim();
char[] phrase1Arr = phrase1.toCharArray();
String phrase2 = "schoolmaster";
phrase2 = (phrase2.toLowerCase()).trim();
ArrayList<Character> phrase2ArrList = convertStringToArraylist(phrase2);
if (phrase1.length() != phrase2.length()) {
System.out.print("There is no anagram present.");
} else {
boolean isFound = true;
for (int i = 0; i < phrase1Arr.length; i++) {
for (int j = 0; j < phrase2ArrList.size(); j++) {
if (phrase1Arr[i] == phrase2ArrList.get(j)) {
System.out.print("There is a common element.\n");
isFound =;
phrase2ArrList.remove(j);
}
}
if (isFound == false) {
System.out.print("There are no anagrams present.");
return;
}
}
System.out.printf("%s is an anagram of %s", phrase1, phrase2);
}
}
public static ArrayList<Character> convertStringToArraylist(String str) {
ArrayList<Character> charList = new ArrayList<Character>();
for (int i = 0; i < str.length(); i++) {
charList.add(str.charAt(i));
}
return charList;
}
}
Two words are anagrams of each other if they contain the same number of characters and the same characters. You should only need to sort the characters in lexicographic order, and determine if all the characters in one string are equal to and in the same order as all of the characters in the other string.
Here's a code example. Look into Arrays in the API to understand what's going on here.
public boolean isAnagram(String firstWord, String secondWord) {
char[] word1 = firstWord.replaceAll("[\\s]", "").toCharArray();
char[] word2 = secondWord.replaceAll("[\\s]", "").toCharArray();
Arrays.sort(word1);
Arrays.sort(word2);
return Arrays.equals(word1, word2);
}
Fastest algorithm would be to map each of the 26 English characters to a unique prime number. Then calculate the product of the string. By the fundamental theorem of arithmetic, 2 strings are anagrams if and only if their products are the same.
If you sort either array, the solution becomes O(n log n). but if you use a hashmap, it's O(n). tested and working.
char[] word1 = "test".toCharArray();
char[] word2 = "tes".toCharArray();
Map<Character, Integer> lettersInWord1 = new HashMap<Character, Integer>();
for (char c : word1) {
int count = 1;
if (lettersInWord1.containsKey(c)) {
count = lettersInWord1.get(c) + 1;
}
lettersInWord1.put(c, count);
}
for (char c : word2) {
int count = -1;
if (lettersInWord1.containsKey(c)) {
count = lettersInWord1.get(c) - 1;
}
lettersInWord1.put(c, count);
}
for (char c : lettersInWord1.keySet()) {
if (lettersInWord1.get(c) != 0) {
return false;
}
}
return true;
Here's a simple fast O(n) solution without using sorting or multiple loops or hash maps. We increment the count of each character in the first array and decrement the count of each character in the second array. If the resulting counts array is full of zeros, the strings are anagrams. Can be expanded to include other characters by increasing the size of the counts array.
class AnagramsFaster{
private static boolean compare(String a, String b){
char[] aArr = a.toLowerCase().toCharArray(), bArr = b.toLowerCase().toCharArray();
if (aArr.length != bArr.length)
return false;
int[] counts = new int[26]; // An array to hold the number of occurrences of each character
for (int i = 0; i < aArr.length; i++){
counts[aArr[i]-97]++; // Increment the count of the character at i
counts[bArr[i]-97]--; // Decrement the count of the character at i
}
// If the strings are anagrams, the counts array will be full of zeros
for (int i = 0; i<26; i++)
if (counts[i] != 0)
return false;
return true;
}
public static void main(String[] args){
System.out.println(compare(args[0], args[1]));
}
}
Lots of people have presented solutions, but I just want to talk about the algorithmic complexity of some of the common approaches:
The simple "sort the characters using Arrays.sort()" approach is going to be O(N log N).
If you use radix sorting, that reduces to O(N) with O(M) space, where M is the number of distinct characters in the alphabet. (That is 26 in English ... but in theory we ought to consider multi-lingual anagrams.)
The "count the characters" using an array of counts is also O(N) ... and faster than radix sort because you don't need to reconstruct the sorted string. Space usage will be O(M).
A "count the characters" using a dictionary, hashmap, treemap, or equivalent will be slower that the array approach, unless the alphabet is huge.
The elegant "product-of-primes" approach is unfortunately O(N^2) in the worst case This is because for long-enough words or phrases, the product of the primes won't fit into a long. That means that you'd need to use BigInteger, and N times multiplying a BigInteger by a small constant is O(N^2).
For a hypothetical large alphabet, the scaling factor is going to be large. The worst-case space usage to hold the product of the primes as a BigInteger is (I think) O(N*logM).
A hashcode based approach is usually O(N) if the words are not anagrams. If the hashcodes are equal, then you still need to do a proper anagram test. So this is not a complete solution.
I would also like to highlight that most of the posted answers assume that each code-point in the input string is represented as a single char value. This is not a valid assumption for code-points outside of the BMP (plane 0); e.g. if an input string contains emojis.
The solutions that make the invalid assumption will probably work most of the time anyway. A code-point outside of the BMP will represented in the string as two char values: a low surrogate and a high surrogate. If the strings contain only one such code-point, we can get away with treating the char values as if they were code-points. However, we can get into trouble when the strings being tested contain 2 or more code-points. Then the faulty algorithms will fail to distinguish some cases. For example, [SH1, SL1, SH2, SL2] versus [SH1, SL2, SH2, SL1] where the SH<n> and SL<2> denote high and low surrogates respectively. The net result will be false anagrams.
Alex Salauyou's answer gives a couple of solutions that will work for all valid Unicode code-points.
O(n) solution without any kind of sorting and using only one map.
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
Map<Character, Integer> occurrencesMap = new HashMap<>();
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
int nrOfCharsInLeft = occurrencesMap.containsKey(charFromLeft) ? occurrencesMap.get(charFromLeft) : 0;
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
int nrOfCharsInRight = occurrencesMap.containsKey(charFromRight) ? occurrencesMap.get(charFromRight) : 0;
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(int occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
and less generic solution but a little bit faster one. You have to place your alphabet here:
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
char letters[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
Map<Character, Integer> occurrencesMap = new HashMap<>();
for (char l : letters) {
occurrencesMap.put(l, 0);
}
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
Integer nrOfCharsInLeft = occurrencesMap.get(charFromLeft);
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
Integer nrOfCharsInRight = occurrencesMap.get(charFromRight);
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(Integer occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
We're walking two equal length strings and tracking the differences between them. We don't care what the differences are, we just want to know if they have the same characters or not. We can do this in O(n/2) without any post processing (or a lot of primes).
public class TestAnagram {
public static boolean isAnagram(String first, String second) {
String positive = first.toLowerCase();
String negative = second.toLowerCase();
if (positive.length() != negative.length()) {
return false;
}
int[] counts = new int[26];
int diff = 0;
for (int i = 0; i < positive.length(); i++) {
int pos = (int) positive.charAt(i) - 97; // convert the char into an array index
if (counts[pos] >= 0) { // the other string doesn't have this
diff++; // an increase in differences
} else { // it does have it
diff--; // a decrease in differences
}
counts[pos]++; // track it
int neg = (int) negative.charAt(i) - 97;
if (counts[neg] <= 0) { // the other string doesn't have this
diff++; // an increase in differences
} else { // it does have it
diff--; // a decrease in differences
}
counts[neg]--; // track it
}
return diff == 0;
}
public static void main(String[] args) {
System.out.println(isAnagram("zMarry", "zArmry")); // true
System.out.println(isAnagram("basiparachromatin", "marsipobranchiata")); // true
System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterone")); // true
System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterons")); // false
System.out.println(isAnagram("zArmcy", "zArmry")); // false
}
}
Yes this code is dependent on the ASCII English character set of lowercase characters but it shouldn't be hard to modify to other languages. You can always use a Map[Character, Int] to track the same information, it'll just be slower.
By using more memory (an HashMap of at most N/2 elements)we do not need to sort the strings.
public static boolean areAnagrams(String one, String two) {
if (one.length() == two.length()) {
String s0 = one.toLowerCase();
String s1 = two.toLowerCase();
HashMap<Character, Integer> chars = new HashMap<Character, Integer>(one.length());
Integer count;
for (char c : s0.toCharArray()) {
count = chars.get(c);
count = Integer.valueOf(count != null ? count + 1 : 1);
chars.put(c, count);
}
for (char c : s1.toCharArray()) {
count = chars.get(c);
if (count == null) {
return false;
} else {
count--;
chars.put(c, count);
}
}
for (Integer i : chars.values()) {
if (i != 0) {
return false;
}
}
return true;
} else {
return false;
}
}
This function is actually running in O(N) ... instead of O(NlogN) for the solution that sorts the strings. If I were to assume that you are going to use only alphabetic characters I could only use an array of 26 ints (from a to z without accents or decorations) instead of the hashmap.
If we define that :
N = |one| + |two|
we do one iteration over N (once over one to increment the counters, and once to decrement them over two).
Then to check the totals we iterate over at mose N/2.
The other algorithms described have one advantage: they do not use extra memory assuming that Arrays.sort uses inplace versions of QuickSort or merge sort. But since we are talking about anagrams I will assume that we are talking about human languages, thus words should not be long enough to give memory issues.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package Algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import javax.swing.JOptionPane;
/**
*
* #author Mokhtar
*/
public class Anagrams {
//Write aprogram to check if two words are anagrams
public static void main(String[] args) {
Anagrams an=new Anagrams();
ArrayList<String> l=new ArrayList<String>();
String result=JOptionPane.showInputDialog("How many words to test anagrams");
if(Integer.parseInt(result) >1)
{
for(int i=0;i<Integer.parseInt(result);i++)
{
String word=JOptionPane.showInputDialog("Enter word #"+i);
l.add(word);
}
System.out.println(an.isanagrams(l));
}
else
{
JOptionPane.showMessageDialog(null, "Can not be tested, \nYou can test two words or more");
}
}
private static String sortString( String w )
{
char[] ch = w.toCharArray();
Arrays.sort(ch);
return new String(ch);
}
public boolean isanagrams(ArrayList<String> l)
{
boolean isanagrams=true;
ArrayList<String> anagrams = null;
HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
for(int i=0;i<l.size();i++)
{
String word = l.get(i);
String sortedWord = sortString(word);
anagrams = map.get( sortedWord );
if( anagrams == null ) anagrams = new ArrayList<String>();
anagrams.add(word);
map.put(sortedWord, anagrams);
}
for(int h=0;h<l.size();h++)
{
if(!anagrams.contains(l.get(h)))
{
isanagrams=false;
break;
}
}
return isanagrams;
//}
}
}
I am a C++ developer and the code below is in C++. I believe the fastest and easiest way to go about it would be the following:
Create a vector of ints of size 26, with all slots initialized to 0, and place each character of the string into the appropriate position in the vector. Remember, the vector is in alphabetical order and so if the first letter in the string is z, it would go in myvector[26]. Note: This can be done using ASCII characters so essentially your code will look something like this:
string s = zadg;
for(int i =0; i < s.size(); ++i){
myvector[s[i] - 'a'] = myvector['s[i] - 'a'] + 1;
}
So inserting all the elements would take O(n) time as you would only traverse the list once. You can now do the exact same thing for the second string and that too would take O(n) time. You can then compare the two vectors by checking to see if the counters in each slot are the same. If they are, that means you had the same number of EACH character in both the strings and thus they are anagrams. The comparing of the two vectors should also take O(n) time as you are only traversing through it once.
Note: The code only works for a single word of characters. If you have spaces, and numbers and symbols, you can just create a vector of size 96 (ASCII characters 32-127) and instead of saying - 'a' you would say - ' ' as the space character is the first one in the ASCII list of characters.
I hope that helps. If i have made a mistake somewhere, please leave a comment.
So far all proposed solutions work with separate char items, not code points. I'd like to propose two solutions to properly handle surrogate pairs as well (those are characters from U+10000 to U+10FFFF, composed of two char items).
1) One-line O(n logn) solution which utilizes Java 8 CharSequence.codePoints() stream:
static boolean areAnagrams(CharSequence a, CharSequence b) {
return Arrays.equals(a.codePoints().sorted().toArray(),
b.codePoints().sorted().toArray());
}
2) Less elegant O(n) solution (in fact, it will be faster only for long strings with low chances to be anagrams):
static boolean areAnagrams(CharSequence a, CharSequence b) {
int len = a.length();
if (len != b.length())
return false;
// collect codepoint occurrences in "a"
Map<Integer, Integer> ocr = new HashMap<>(64);
a.codePoints().forEach(c -> ocr.merge(c, 1, Integer::sum));
// for each codepoint in "b", look for matching occurrence
for (int i = 0, c = 0; i < len; i += Character.charCount(c)) {
int cc = ocr.getOrDefault((c = Character.codePointAt(b, i)), 0);
if (cc == 0)
return false;
ocr.put(c, cc - 1);
}
return true;
}
Thanks for pointing out to make comment, while making comment I found that there was incorrect logic. I corrected the logic and added comment for each piece of code.
// Time complexity: O(N) where N is number of character in String
// Required space :constant space.
// will work for string that contains ASCII chars
private static boolean isAnagram(String s1, String s2) {
// if length of both string's are not equal then they are not anagram of each other
if(s1.length() != s2.length())return false;
// array to store the presence of a character with number of occurrences.
int []seen = new int[256];
// initialize the array with zero. Do not need to initialize specifically since by default element will initialized by 0.
// Added this is just increase the readability of the code.
Arrays.fill(seen, 0);
// convert each string to lower case if you want to make ABC and aBC as anagram, other wise no need to change the case.
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
// iterate through the first string and count the occurrences of each character
for(int i =0; i < s1.length(); i++){
seen[s1.charAt(i)] = seen[s1.charAt(i)] +1;
}
// iterate through second string and if any char has 0 occurrence then return false, it mean some char in s2 is there that is not present in s1.
// other wise reduce the occurrences by one every time .
for(int i =0; i < s2.length(); i++){
if(seen[s2.charAt(i)] ==0)return false;
seen[s2.charAt(i)] = seen[s2.charAt(i)]-1;
}
// now if both string have same occurrence of each character then the seen array must contains all element as zero. if any one has non zero element return false mean there are
// some character that either does not appear in one of the string or/and mismatch in occurrences
for(int i = 0; i < 256; i++){
if(seen[i] != 0)return false;
}
return true;
}
IMHO, the most efficient solution was provided by #Siguza, I have extended it to cover strings with space e.g: "William Shakespeare", "I am a weakish speller", "School master", "The classroom"
public int getAnagramScore(String word, String anagram) {
if (word == null || anagram == null) {
throw new NullPointerException("Both, word and anagram, must be non-null");
}
char[] wordArray = word.trim().toLowerCase().toCharArray();
char[] anagramArray = anagram.trim().toLowerCase().toCharArray();
int[] alphabetCountArray = new int[26];
int reference = 'a';
for (int i = 0; i < wordArray.length; i++) {
if (!Character.isWhitespace(wordArray[i])) {
alphabetCountArray[wordArray[i] - reference]++;
}
}
for (int i = 0; i < anagramArray.length; i++) {
if (!Character.isWhitespace(anagramArray[i])) {
alphabetCountArray[anagramArray[i] - reference]--;
}
}
for (int i = 0; i < 26; i++)
if (alphabetCountArray[i] != 0)
return 0;
return word.length();
}
// When this method returns 0 means strings are Anagram, else Not.
public static int isAnagram(String str1, String str2) {
int value = 0;
if (str1.length() == str2.length()) {
for (int i = 0; i < str1.length(); i++) {
value = value + str1.charAt(i);
value = value - str2.charAt(i);
}
} else {
value = -1;
}
return value;
}
Many complicated answers here. Base on the accepted answer and the comment mentioning the 'ac'-'bb' issue assuming A=65 B=66 C=67, we could simply use the square of each integer that represent a char and solve the problem:
public boolean anagram(String s, String t) {
if(s.length() != t.length())
return false;
int value = 0;
for(int i = 0; i < s.length(); i++){
value += ((int)s.charAt(i))^2;
value -= ((int)t.charAt(i))^2;
}
return value == 0;
}
A similar answer may have been posted in C++, here it is again in Java. Note that the most elegant way would be to use a Trie to store the characters in sorted order, however, that's a more complex solution. One way is to use a hashset to store all the words we are comparing and then compare them one by one. To compare them, make an array of characters with the index representing the ANCII value of the characters (using a normalizer since ie. ANCII value of 'a' is 97) and the value representing the occurrence count of that character. This will run in O(n) time and use O(m*z) space where m is the size of the currentWord and z the size for the storedWord, both for which we create a Char[].
public static boolean makeAnagram(String currentWord, String storedWord){
if(currentWord.length() != storedWord.length()) return false;//words must be same length
Integer[] currentWordChars = new Integer[totalAlphabets];
Integer[] storedWordChars = new Integer[totalAlphabets];
//create a temp Arrays to compare the words
storeWordCharacterInArray(currentWordChars, currentWord);
storeWordCharacterInArray(storedWordChars, storedWord);
for(int i = 0; i < totalAlphabets; i++){
//compare the new word to the current charList to see if anagram is possible
if(currentWordChars[i] != storedWordChars[i]) return false;
}
return true;//and store this word in the HashSet of word in the Heap
}
//for each word store its characters
public static void storeWordCharacterInArray(Integer[] characterList, String word){
char[] charCheck = word.toCharArray();
for(char c: charCheck){
Character cc = c;
int index = cc.charValue()-indexNormalizer;
characterList[index] += 1;
}
}
How a mathematician might think about the problem before writing any code:
The relation "are anagrams" between strings is an equivalence relation, so partitions the set of all strings into equivalence classes.
Suppose we had a rule to choose a representative (crib) from each class, then it's easy to test whether two classes are the same by comparing their representatives.
An obvious representative for a set of strings is "the smallest element by lexicographic order", which is easy to compute from any element by sorting. For example, the representative of the anagram class containing 'hat' is 'aht'.
In your example "schoolmaster" and "theclassroom" are anagrams because they are both in the anagram class with crib "acehlmoorsst".
In pseudocode:
>>> def crib(word):
... return sorted(word)
...
>>> crib("schoolmaster") == crib("theclassroom")
True
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Check if Anagram by Prime Number Logic
* #author Pallav
*
*/
public class Anagram {
public static void main(String args[]) {
System.out.println(isAnagram(args[0].toUpperCase(),
args[1].toUpperCase()));
}
/**
*
* #param word : The String 1
* #param anagram_word : The String 2 with which Anagram to be verified
* #return true or false based on Anagram
*/
public static Boolean isAnagram(String word, String anagram_word) {
//If length is different return false
if (word.length() != anagram_word.length()) {
return false;
}
char[] words_char = word.toCharArray();//Get the Char Array of First String
char[] anagram_word_char = anagram_word.toCharArray();//Get the Char Array of Second String
int words_char_num = 1;//Initialize Multiplication Factor to 1
int anagram_word_num = 1;//Initialize Multiplication Factor to 1 for String 2
Map<Character, Integer> wordPrimeMap = wordPrimeMap();//Get the Prime numbers Mapped to each alphabets in English
for (int i = 0; i < words_char.length; i++) {
words_char_num *= wordPrimeMap.get(words_char[i]);//get Multiplication value for String 1
}
for (int i = 0; i < anagram_word_char.length; i++) {
anagram_word_num *= wordPrimeMap.get(anagram_word_char[i]);//get Multiplication value for String 2
}
return anagram_word_num == words_char_num;
}
/**
* Get the Prime numbers Mapped to each alphabets in English
* #return
*/
public static Map<Character, Integer> wordPrimeMap() {
List<Integer> primes = primes(26);
int k = 65;
Map<Character, Integer> map = new TreeMap<Character, Integer>();
for (int i = 0; i < primes.size(); i++) {
Character character = (char) k;
map.put(character, primes.get(i));
k++;
}
// System.out.println(map);
return map;
}
/**
* get first N prime Numbers where Number is greater than 2
* #param N : Number of Prime Numbers
* #return
*/
public static List<Integer> primes(Integer N) {
List<Integer> primes = new ArrayList<Integer>();
primes.add(2);
primes.add(3);
int n = 5;
int k = 0;
do {
boolean is_prime = true;
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
is_prime = false;
break;
}
}
if (is_prime == true) {
primes.add(n);
}
n++;
// System.out.println(k);
} while (primes.size() < N);
// }
return primes;
}
}
Here is my solution.First explode the strings into char arrays then sort them and then comparing if they are equal or not. I guess time complexity of this code is O(a+b).if a=b we can say O(2A)
public boolean isAnagram(String s1, String s2) {
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
if (s1.length() != s2.length())
return false;
char arr1[] = s1.toCharArray();
char arr2[] = s2.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
for (char c : arr1) {
sb1.append(c);
}
for (char c : arr2) {
sb2.append(c);
}
System.out.println(sb1.toString());
System.out.println(sb2.toString());
if (sb1.toString().equals(sb2.toString()))
return true;
else
return false;
}
There are 3 solution i can think of :
Using sorting
# O(NlogN) + O(MlogM) time, O(1) space
def solve_by_sort(word1, word2):
return sorted(word1) == sorted(word2)
Using letter frequency count
# O(N+M) time, O(N+M) space
def solve_by_letter_frequency(word1, word2):
from collections import Counter
return Counter(word1) == Counter(word2)
Using the concept of prime factorization. (assign primes to each letter)
import operator
from functools import reduce
# O(N) time, O(1) space - prime factorization
def solve_by_prime_number_hash(word1, word2):
return get_prime_number_hash(word1) == get_prime_number_hash(word2)
def get_prime_number_hash(word):
letter_code = {'a': 2, 'b': 3, 'c': 5, 'd': 7, 'e': 11, 'f': 13, 'g': 17, 'h': 19, 'i': 23, 'j': 29, 'k': 31,'l': 37, 'm': 41, 'n': 43,'o': 47, 'p': 53, 'q': 59, 'r': 61, 's': 67, 't': 71, 'u': 73, 'v': 79, 'w': 83, 'x': 89, 'y': 97,'z': 101}
return 0 if not word else reduce(operator.mul, [letter_code[letter] for letter in word])
I have put more detailed analysis of these in my medium story.
Sorting approach is not the best one. It takes O(n) space and O(nlogn) time. Instead, make a hash map of characters and count them (increment characters that appear in the first string and decrement characters that appear in the second string). When some count reaches zero, remove it from hash. Finally, if two strings are anagrams, then the hash table will be empty in the end - otherwise it will not be empty.
Couple of important notes: (1) Ignore letter case and (2) Ignore white space.
Here is the detailed analysis and implementation in C#: Testing If Two Strings are Anagrams
Some other solution without sorting.
public static boolean isAnagram(String s1, String s2){
//case insensitive anagram
StringBuffer sb = new StringBuffer(s2.toLowerCase());
for (char c: s1.toLowerCase().toCharArray()){
if (Character.isLetter(c)){
int index = sb.indexOf(String.valueOf(c));
if (index == -1){
//char does not exist in other s2
return false;
}
sb.deleteCharAt(index);
}
}
for (char c: sb.toString().toCharArray()){
//only allow whitespace as left overs
if (!Character.isWhitespace(c)){
return false;
}
}
return true;
}
A simple method to figure out whether the testString is an anagram of the baseString.
private static boolean isAnagram(String baseString, String testString){
//Assume that there are no empty spaces in either string.
if(baseString.length() != testString.length()){
System.out.println("The 2 given words cannot be anagram since their lengths are different");
return false;
}
else{
if(baseString.length() == testString.length()){
if(baseString.equalsIgnoreCase(testString)){
System.out.println("The 2 given words are anagram since they are identical.");
return true;
}
else{
List<Character> list = new ArrayList<>();
for(Character ch : baseString.toLowerCase().toCharArray()){
list.add(ch);
}
System.out.println("List is : "+ list);
for(Character ch : testString.toLowerCase().toCharArray()){
if(list.contains(ch)){
list.remove(ch);
}
}
if(list.isEmpty()){
System.out.println("The 2 words are anagrams");
return true;
}
}
}
}
return false;
}
Sorry, the solution is in C#, but I think the different elements used to arrive at the solution is quite intuitive. Slight tweak required for hyphenated words but for normal words it should work fine.
internal bool isAnagram(string input1,string input2)
{
Dictionary<char, int> outChars = AddToDict(input2.ToLower().Replace(" ", ""));
input1 = input1.ToLower().Replace(" ","");
foreach(char c in input1)
{
if (outChars.ContainsKey(c))
{
if (outChars[c] > 1)
outChars[c] -= 1;
else
outChars.Remove(c);
}
}
return outChars.Count == 0;
}
private Dictionary<char, int> AddToDict(string input)
{
Dictionary<char, int> inputChars = new Dictionary<char, int>();
foreach(char c in input)
{
if(inputChars.ContainsKey(c))
{
inputChars[c] += 1;
}
else
{
inputChars.Add(c, 1);
}
}
return inputChars;
}
I saw that no one has used the "hashcode" approach to find out the anagrams. I found my approach little different than the approaches discussed above hence thought of sharing it. I wrote the below code to find the anagrams which works in O(n).
/**
* This class performs the logic of finding anagrams
* #author ripudam
*
*/
public class AnagramTest {
public static boolean isAnagram(final String word1, final String word2) {
if (word1 == null || word2 == null || word1.length() != word2.length()) {
return false;
}
if (word1.equals(word2)) {
return true;
}
final AnagramWrapper word1Obj = new AnagramWrapper(word1);
final AnagramWrapper word2Obj = new AnagramWrapper(word2);
if (word1Obj.equals(word2Obj)) {
return true;
}
return false;
}
/*
* Inner class to wrap the string received for anagram check to find the
* hash
*/
static class AnagramWrapper {
String word;
public AnagramWrapper(final String word) {
this.word = word;
}
#Override
public boolean equals(final Object obj) {
return hashCode() == obj.hashCode();
}
#Override
public int hashCode() {
final char[] array = word.toCharArray();
int hashcode = 0;
for (final char c : array) {
hashcode = hashcode + (c * c);
}
return hashcode;
}
}
}
Here is another approach using HashMap in Java
public static boolean isAnagram(String first, String second) {
if (first == null || second == null) {
return false;
}
if (first.length() != second.length()) {
return false;
}
return doCheckAnagramUsingHashMap(first.toLowerCase(), second.toLowerCase());
}
private static boolean doCheckAnagramUsingHashMap(final String first, final String second) {
Map<Character, Integer> counter = populateMap(first, second);
return validateMap(counter);
}
private static boolean validateMap(Map<Character, Integer> counter) {
for (int val : counter.values()) {
if (val != 0) {
return false;
}
}
return true;
}
Here is the test case
#Test
public void anagramTest() {
assertTrue(StringUtil.isAnagram("keep" , "PeeK"));
assertFalse(StringUtil.isAnagram("Hello", "hell"));
assertTrue(StringUtil.isAnagram("SiLeNt caT", "LisTen cat"));
}
private static boolean checkAnagram(String s1, String s2) {
if (s1 == null || s2 == null) {
return false;
} else if (s1.length() != s2.length()) {
return false;
}
char[] a1 = s1.toCharArray();
char[] a2 = s2.toCharArray();
int length = s2.length();
int s1Count = 0;
int s2Count = 0;
for (int i = 0; i < length; i++) {
s1Count+=a1[i];
s2Count+=a2[i];
}
return s2Count == s1Count ? true : false;
}
The simplest solution with complexity O(N) is using Map.
public static Boolean checkAnagram(String string1, String string2) {
Boolean anagram = true;
Map<Character, Integer> map1 = new HashMap<>();
Map<Character, Integer> map2 = new HashMap<>();
char[] chars1 = string1.toCharArray();
char[] chars2 = string2.toCharArray();
for(int i=0; i<chars1.length; i++) {
if(map1.get(chars1[i]) == null) {
map1.put(chars1[i], 1);
} else {
map1.put(chars1[i], map1.get(chars1[i])+1);
}
if(map2.get(chars2[i]) == null) {
map2.put(chars2[i], 1);
} else {
map2.put(chars2[i], map2.get(chars2[i])+1);
}
}
Set<Map.Entry<Character, Integer>> entrySet1 = map1.entrySet();
Set<Map.Entry<Character, Integer>> entrySet2 = map2.entrySet();
for(Map.Entry<Character, Integer> entry:entrySet1) {
if(entry.getValue() != map2.get(entry.getKey())) {
anagram = false;
break;
}
}
return anagram;
}
let's take a question: Given two strings s and t, write a function to determine if t is an anagram of s.
For example,
s = "anagram", t = "nagaram", return true.
s = "rat", t = "car", return false.
Method 1(Using HashMap ):
public class Method1 {
public static void main(String[] args) {
String a = "protijayi";
String b = "jayiproti";
System.out.println(isAnagram(a, b ));// output => true
}
private static boolean isAnagram(String a, String b) {
Map<Character ,Integer> map = new HashMap<>();
for( char c : a.toCharArray()) {
map.put(c, map.getOrDefault(c, 0 ) + 1 );
}
for(char c : b.toCharArray()) {
int count = map.getOrDefault(c, 0);
if(count == 0 ) {return false ; }
else {map.put(c, count - 1 ) ; }
}
return true;
}
}
Method 2 :
public class Method2 {
public static void main(String[] args) {
String a = "protijayi";
String b = "jayiproti";
System.out.println(isAnagram(a, b));// output=> true
}
private static boolean isAnagram(String a, String b) {
int[] alphabet = new int[26];
for(int i = 0 ; i < a.length() ;i++) {
alphabet[a.charAt(i) - 'a']++ ;
}
for (int i = 0; i < b.length(); i++) {
alphabet[b.charAt(i) - 'a']-- ;
}
for( int w : alphabet ) {
if(w != 0 ) {return false;}
}
return true;
}
}
Method 3 :
public class Method3 {
public static void main(String[] args) {
String a = "protijayi";
String b = "jayiproti";
System.out.println(isAnagram(a, b ));// output => true
}
private static boolean isAnagram(String a, String b) {
char[] ca = a.toCharArray() ;
char[] cb = b.toCharArray();
Arrays.sort( ca );
Arrays.sort( cb );
return Arrays.equals(ca , cb );
}
}
Method 4 :
public class AnagramsOrNot {
public static void main(String[] args) {
String a = "Protijayi";
String b = "jayiProti";
isAnagram(a, b);
}
private static void isAnagram(String a, String b) {
Map<Integer, Integer> map = new LinkedHashMap<>();
a.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) + 1));
System.out.println(map);
b.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) - 1));
System.out.println(map);
if (map.values().contains(0)) {
System.out.println("Anagrams");
} else {
System.out.println("Not Anagrams");
}
}
}
In Python:
def areAnagram(a, b):
if len(a) != len(b): return False
count1 = [0] * 256
count2 = [0] * 256
for i in a:count1[ord(i)] += 1
for i in b:count2[ord(i)] += 1
for i in range(256):
if(count1[i] != count2[i]):return False
return True
str1 = "Giniiii"
str2 = "Protijayi"
print(areAnagram(str1, str2))
Let's take another famous Interview Question: Group the Anagrams from a given String:
public class GroupAnagrams {
public static void main(String[] args) {
String a = "Gini Gina Protijayi iGin aGin jayiProti Soudipta";
Map<String, List<String>> map = Arrays.stream(a.split(" ")).collect(Collectors.groupingBy(GroupAnagrams::sortedString));
System.out.println("MAP => " + map);
map.forEach((k,v) -> System.out.println(k +" and the anagrams are =>" + v ));
/*
Look at the Map output:
MAP => {Giin=[Gini, iGin], Paiijorty=[Protijayi, jayiProti], Sadioptu=[Soudipta], Gain=[Gina, aGin]}
As we can see, there are multiple Lists. Hence, we have to use a flatMap(List::stream)
Now, Look at the output:
Paiijorty and the anagrams are =>[Protijayi, jayiProti]
Now, look at this output:
Sadioptu and the anagrams are =>[Soudipta]
List contains only word. No anagrams.
That means we have to work with map.values(). List contains all the anagrams.
*/
String stringFromMapHavingListofLists = map.values().stream().flatMap(List::stream).collect(Collectors.joining(" "));
System.out.println(stringFromMapHavingListofLists);
}
public static String sortedString(String a) {
String sortedString = a.chars().sorted()
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
return sortedString;
}
/*
* The output : Gini iGin Protijayi jayiProti Soudipta Gina aGin
* All the anagrams are side by side.
*/
}
Now to Group Anagrams in Python is again easy.We have to :
Sort the lists. Then, Create a dictionary. Now dictionary will tell us where are those anagrams are( Indices of Dictionary). Then values of the dictionary is the actual indices of the anagrams.
def groupAnagrams(words):
# sort each word in the list
A = [''.join(sorted(word)) for word in words]
dict = {}
for indexofsamewords, names in enumerate(A):
dict.setdefault(names, []).append(indexofsamewords)
print(dict)
#{'AOOPR': [0, 2, 5, 11, 13], 'ABTU': [1, 3, 4], 'Sorry': [6], 'adnopr': [7], 'Sadioptu': [8, 16], ' KPaaehiklry': [9], 'Taeggllnouy': [10], 'Leov': [12], 'Paiijorty': [14, 18], 'Paaaikpr': [15], 'Saaaabhmryz': [17], ' CNaachlortttu': [19], 'Saaaaborvz': [20]}
for index in dict.values():
print([words[i] for i in index])
if __name__ == '__main__':
# list of words
words = ["ROOPA","TABU","OOPAR","BUTA","BUAT" , "PAROO","Soudipta",
"Kheyali Park", "Tollygaunge", "AROOP","Love","AOORP", "Protijayi","Paikpara","dipSouta","Shyambazaar",
"jayiProti", "North Calcutta", "Sovabazaar"]
groupAnagrams(words)
The Output :
['ROOPA', 'OOPAR', 'PAROO', 'AROOP', 'AOORP']
['TABU', 'BUTA', 'BUAT']
['Soudipta', 'dipSouta']
['Kheyali Park']
['Tollygaunge']
['Love']
['Protijayi', 'jayiProti']
['Paikpara']
['Shyambazaar']
['North Calcutta']
['Sovabazaar']
Another Important Anagram Question : Find the Anagram occuring Max. number of times.
In the Example, ROOPA is the word which has occured maximum number of times.
Hence, ['ROOPA' 'OOPAR' 'PAROO' 'AROOP' 'AOORP'] will be the final output.
from sqlite3 import collections
from statistics import mode, mean
import numpy as np
# list of words
words = ["ROOPA","TABU","OOPAR","BUTA","BUAT" , "PAROO","Soudipta",
"Kheyali Park", "Tollygaunge", "AROOP","Love","AOORP",
"Protijayi","Paikpara","dipSouta","Shyambazaar",
"jayiProti", "North Calcutta", "Sovabazaar"]
print(".....Method 1....... ")
sortedwords = [''.join(sorted(word)) for word in words]
print(sortedwords)
print("...........")
LongestAnagram = np.array(words)[np.array(sortedwords) == mode(sortedwords)]
# Longest anagram
print("Longest anagram by Method 1:")
print(LongestAnagram)
print(".....................................................")
print(".....Method 2....... ")
A = [''.join(sorted(word)) for word in words]
dict = {}
for indexofsamewords,samewords in enumerate(A):
dict.setdefault(samewords,[]).append(samewords)
#print(dict)
#{'AOOPR': ['AOOPR', 'AOOPR', 'AOOPR', 'AOOPR', 'AOOPR'], 'ABTU': ['ABTU', 'ABTU', 'ABTU'], 'Sadioptu': ['Sadioptu', 'Sadioptu'], ' KPaaehiklry': [' KPaaehiklry'], 'Taeggllnouy': ['Taeggllnouy'], 'Leov': ['Leov'], 'Paiijorty': ['Paiijorty', 'Paiijorty'], 'Paaaikpr': ['Paaaikpr'], 'Saaaabhmryz': ['Saaaabhmryz'], ' CNaachlortttu': [' CNaachlortttu'], 'Saaaaborvz': ['Saaaaborvz']}
aa = max(dict.items() , key = lambda x : len(x[1]))
print("aa => " , aa)
word, anagrams = aa
print("Longest anagram by Method 2:")
print(" ".join(anagrams))
The Output :
.....Method 1.......
['AOOPR', 'ABTU', 'AOOPR', 'ABTU', 'ABTU', 'AOOPR', 'Sadioptu', ' KPaaehiklry', 'Taeggllnouy', 'AOOPR', 'Leov', 'AOOPR', 'Paiijorty', 'Paaaikpr', 'Sadioptu', 'Saaaabhmryz', 'Paiijorty', ' CNaachlortttu', 'Saaaaborvz']
...........
Longest anagram by Method 1:
['ROOPA' 'OOPAR' 'PAROO' 'AROOP' 'AOORP']
.....................................................
.....Method 2.......
aa => ('AOOPR', ['AOOPR', 'AOOPR', 'AOOPR', 'AOOPR', 'AOOPR'])
Longest anagram by Method 2:
AOOPR AOOPR AOOPR AOOPR AOOPR
This could be the simple function call
A mix of functional Code and Imperative style of code
static boolean isAnagram(String a, String b) {
String sortedA = "";
Object[] aArr = a.toLowerCase().chars().sorted().mapToObj(i -> (char) i).toArray();
for (Object o: aArr) {
sortedA = sortedA.concat(o.toString());
}
String sortedB = "";
Object[] bArr = b.toLowerCase().chars().sorted().mapToObj(i -> (char) i).toArray();
for (Object o: bArr) {
sortedB = sortedB.concat(o.toString());
}
if(sortedA.equals(sortedB))
return true;
else
return false;
}

Categories