Facebook Interview Question
iOS DevelopersCountry: United States
Interview Type: Phone Interview
You don't even need to sort the characters in the words - just store them in a multiset (one per word) and put these multisets to a hashtable to reach O(n).
Provided it's related to OS X or iOS and the power of Foundation can be used:
@import Foundation;
BOOL containsAnnagrams(NSArray* words) {
NSMutableSet* characterSets = [NSMutableSet set];
for (NSString* word in words) {
NSCharacterSet* characterSet = [NSCharacterSet characterSetWithCharactersInString:word];
if ([characterSets containsObject:characterSet]) {
return YES;
}
[characterSets addObject:characterSet];
}
return NO;
}
public class Anagrams {
public static void main(String[] args) {
String[] arr = new String[] {"bat","cat","tab","put"};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
if(i!=j){
boolean contains = anagrams(arr[j],arr[i]);
if(contains){
System.out.println("The string "+ arr[i] + " is anagram of "+ arr[j]);
}
}
}
}
}
private static boolean anagrams(String string1, String string2) {
char[] c1 = string1.toCharArray();
char[] c2 = string2.toCharArray();
boolean contains = false;
for (int i = 0; i < c1.length; i++) {
contains = false;
for (int j = 0; j < c2.length; j++) {
if(c2[j] == c1[i]){
contains = true;
break;
}
}if(!contains){
break;
}
}
return contains;
}
}
Hi
Assume list of input Strings {"abc","bcd","bca"}
My answer is find out the hash of each string and check with others if it is equal then both are anagrams.
For example:
"abc" hash is 'a'+'b'+'c' = 97+98+99 = 294
"bcd" hash is 'b'+'c'+'d' = 98+99+100 = 297
"bca" hash is 'b'+'c'+'a' = 98+99+97 = 294
From above example we can conclude that "abc" and "bca" are anagrams
If this is wrong let me.
Thanks
void groupAnagrams(NSArray *array) {
NSMutableDictionary *group = [[NSMutableDictionary alloc] initWithCapacity:[array count]];
for (NSString *s in array) {
const char *c = [s UTF8String];
int sum = sumCharacters(c);
NSMutableArray *anagrams = [group objectForKey:[NSNumber numberWithInt:sum]];
if (anagrams) {
[anagrams addObject:s];
} else {
NSMutableArray *anagrams = [[NSMutableArray alloc] initWithCapacity:10];
[anagrams addObject:s];
[group setObject:anagrams forKey:[NSNumber numberWithInt:sum]];
}
}
for (NSNumber *n in group) {
NSArray *anagrams = [group objectForKey:n];
printf("[");
for (NSString *s in anagrams) {
printf("%s,", [s UTF8String]);
}
printf("]\n");
}
}
int sumCharacters(const char *c) {
int charValues[26] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101};
int intValues[10] = {103, 107, 109, 113, 127, 131, 137, 139, 149, 151};
int spaceValue = 157;
int commaValue = 163;
int sum = 0;
for (int i=0; c[i] != '\0';i++) {
if (c[i] >= 'a' && c[i] <= 'z') {
sum += charValues[c[i] - 'a'];
} else if (c[i] >='0' && c[i] <= '9') {
sum += intValues[c[i] - '0'];
} else if (c[i] == ' '){
sum += spaceValue;
} else if (c[i] == ','){
sum += commaValue;
}
}
return sum;
}
convert "bat" to "a1b1t1", so the algo is:
std::string strtostr(std::string s)
{
int a[26] = {0};
int l = s.length();
for (int i = 0; i < l; ++i)
{
a[s[i] - 'a']++;
}
std::string result;
for (int i = 0; i < 26; ++i)
{
if (a[i] > 0)
{
result += ('a' + i);
result += ('0' + a[i]);
}
}
return result;
}
bool has_anagrams(std::vector<std::string>& strset)
{
std::set<std::string> wordset;
int size = strset.size();
for (int i = 0; i < size; ++i)
{
std::string k = strtostr(strset[i]);
if (wordset.find(k) != wordset.end())
{
return true;
}
else
wordset.insert(k);
}
return false;
}
How about this?
list of string -> Map of Integer to List.
For key of this map, I will use hash function which just take ascii value of chars in each string.
It's like pre-processing.
If one of those key has a value of list whose size is bigger than 1, it means that this list 'may' have a candidate of anagram.
In this case, I sort every string in this list, and put them into a 'Set'. If there are collisions,
it means "there is a anagram".
public static boolean checkAnagrams(String[] strings) {
Map<Integer, List<String>> result = createMap(strings);
for (int key : result.keySet()) {
if (result.get(key).size() > 1) {
Set<String> set = new HashSet<>();
for (String s : result.get(key)) {
char[] target = s.toCharArray();
Arrays.sort(target);
if (set.contains(new String(target))) {
return true;
}
set.add(new String(target));
}
}
}
return false;
}
private static Map<Integer, List<String>> createMap(String[] strings) {
System.out.println("Come to createMap");
Map<Integer, List<String>> info = new HashMap<>();
for (String s: strings) {
int currentHash = getHash(s);
List<String> results;
if (info.containsKey(currentHash)) {
results = info.get(currentHash);
} else {
results = new ArrayList<>();
}
results.add(s);
info.put(currentHash, results);
}
return info;
}
private static int getHash(String input) {
int hashValue = 0;
for (char c : input.toCharArray()) {
hashValue += (int) c;
}
return hashValue;
}
PHP version
function hasAnagrams($stringsArray) {
// keep a hash of the ordered words
$stringsOrderedHash = array();
foreach ($stringsArray as $word) {
// skip blanks
if (!$word) {
continue;
}
// lowercase the word, as anagrams should match regardless of case???
$word = strtolower($word);
// convert word to array
$wordArray = str_split($word);
// sort resultant letters
sort($wordArray);
$wordSorted = implode($wordArray);
// key exists, has anagrams
if (isset($stringsOrderedHash[$wordSorted])) {
return true;
}
// add to hash as key to allow index lookup
$stringsOrderedHash[$wordSorted] = 1;
}
// ran out of words to check, so no anagrams
return false;
}
static boolean hasAnagram(String [] list)
{
for(int i=0;i<list.length;i++)
{
for(int j=0;j<list.length;j++)
{
if(i==j);
else
{
if(list[i].length()-list[j].length()==0)
{
for(int m=0;m<list[i].length();m++)
{
for(int n=0;n<list[i].length();n++)
{
if(list[i].charAt(m)==list[j].charAt(n))
{
count++;
}
}
}
if(count==list[i].length())
val=1;
count=0;
}
}
}
}
if(val==1)
return true;
else
return false;
}
private static boolean verifyAnagramExistence(String[] str) {
boolean isAnagramExist = false;
int[] ascii = new int[str.length];
int index = 0;
for(String s : str){
int value = getAsciiValue(s);
if(!isAlreadyExist(ascii,value))
ascii[index++] = value;
else{
isAnagramExist = true;
break;
}
}
return isAnagramExist;
}
private static int getAsciiValue(String s) {
int sum = 0;
for(char ch : s.toCharArray()){
sum += String.valueOf(ch).codePointAt(0);
}
return sum;
}
private static boolean isAlreadyExist(int[] ascii, int value) {
for(int number : ascii){
if(number == value)
return true;
}
return false;
}
1. Assign a prime number to each letter (a = 2, b = 3, c = 5, ...)
2. For each word compute the hash as the product of the prime numbers associated with each letter
3. Store the hash in a set - if it's already there, you have anagrams
Time: O(n), where n is the number of letters, Space: O(n), where n is the number of words.
public class TestProgram {
static final String[] INPUT_ARRAY = { "bag", "bat", "tab" };
// static final String[] INPUT_ARRAY = {"gab","bat","laf"};
public static void main(String[] args) {
boolean result = false;
for (int i = 0; i < INPUT_ARRAY.length - 1; i++) {
StringBuilder word = new StringBuilder(INPUT_ARRAY[i]);
StringBuilder nextWord = new StringBuilder(INPUT_ARRAY[i + 1]);
if (word.length() != nextWord.length())
continue;
for (int j = 0; j < word.length();) {
String chr = word.charAt(j) + "";
int index = nextWord.indexOf(chr);
if (index == -1)
break;
word.deleteCharAt(j);
nextWord.deleteCharAt(index);
}
if (word.length() == 0) {
result = true;
break;
}
}
System.out.println(String.valueOf(result).toUpperCase());
}
}
This is what i think can be a good match for ios.
BOOL isAnagram(NSArray *input) {
NSMutableDictionary *anagrams = [NSMutableDictionary dictionary];
__block BOOL isAnagram = NO;
[input enumerateObjectsUsingBlock:^(NSString *word, NSUInteger idx, BOOL *stop) {
NSMutableArray *characters = [NSMutableArray array];
for (NSUInteger i = 0; i < word.length; i++) {
[characters addObject:[word substringWithRange:NSMakeRange(i,1)]];
}
// now sort the word
NSArray *sortedCharacters = [characters sortedArrayUsingSelector:@selector(compare:)];
NSString *sortedWord = [sortedCharacters componentsJoinedByString:@""];
if (anagrams[sortedWord] != nil) {
isAnagram = YES;
*stop = YES;
} else {
anagrams[sortedWord] = sortedWord;
}
}];
return isAnagram;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *input = @[@"bag", @"bat", @"tab"];
if (isAnagram(input)) {
NSLog(@"input is anagram");
} else {
NSLog(@"no Anagrams");
}
}
return 0;
}
can't find a better way to get the characters out
Use a hash map to store the normalized version of a word, which is (char->count). Use a hash set to store all the normalized (char->count) pairs. The hash set need to implement a Comparator<Map<Char, Integer>> interface to tell if two hash maps have the same key set and count sets. The function can return true at first occurrence of matching hash maps. Time complexity is O(n), space complexity is O(n).
public static void main(String[] args){
String[] s={"bak","bag","tab"};
System.out.println(solve(s));
}
static boolean solve(String[] s){
for(int i=0;i<s.length;i++){
String tmp=new StringBuilder(s[i]).reverse().toString();
for(int j=0;j<s.length;j++){
if(i==j)
continue;
if(tmp.equals(s[j]))
return true;
else
continue;
}
}
return false;
}
- (BOOL)arrayContainsAnagrams:(NSArray*)arrayOfWords
{
BOOL containsAnagrams = FALSE;
for (NSString *word in arrayOfWords)
{
for (NSString *anotherWord in arrayOfWords)
{
if ((word != anotherWord) && (word.length>0) && (word.length == anotherWord.length))
{
BOOL foundAllLetters = TRUE;
for (int a = 0; a < word.length; a++)
{
NSString *subString = [word substringWithRange:NSMakeRange(a, 1)];
if ([anotherWord rangeOfString:subString].location == NSNotFound)
{
foundAllLetters = FALSE;
break;
}
}
containsAnagrams = foundAllLetters;
}
}
}
return containsAnagrams;
}
- (BOOL)arrayContainsAnagrams:(NSArray*)arrayOfWords
{
BOOL containsAnagrams = FALSE;
for (NSString *word in arrayOfWords)
{
for (NSString *anotherWord in arrayOfWords)
{
if ((word != anotherWord) && (word.length>0) && (word.length == anotherWord.length))
{
BOOL foundAllLetters = TRUE;
for (int a = 0; a < word.length; a++)
{
NSString *subString = [word substringWithRange:NSMakeRange(a, 1)];
if ([anotherWord rangeOfString:subString].location == NSNotFound)
{
foundAllLetters = FALSE;
break;
}
}
containsAnagrams = foundAllLetters;
}
}
}
return containsAnagrams;
}
NSArray *givenArray1 = @[@"bag", @"bat", @"tab"];
NSMutableArray *reversedArray = [NSMutableArray new];
[givenArray1 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *currentString = obj;
NSMutableString *reversedString = [NSMutableString new];
[currentString enumerateSubstringsInRange:NSMakeRange(0, currentString.length) options:NSStringEnumerationByComposedCharacterSequences|NSStringEnumerationReverse usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
[reversedArray addObject:reversedString];
}];
NSString *foundValue = [givenArray1 firstObjectCommonWithArray:reversedArray];
if (foundValue) {
return YES;
}else{
return NO;
}
Compute a character frequency table from the first word, then check that every other word creates the same table;
int *charFrequencyTable(const char *s, int len) {
int *charTable = (int*) malloc(256 * sizeof(int));
int i;
for (i = 0; i < 256; i++) {
charTable[i] = 0;
}
for (i = 0; i < len; i++) {
charTable[i]++;
}
return charTable;
}
small int equalCharTables(int *t1, int *t2, int len) {
int i;
for (i = 0; i < len; i++) {
if (t1[i] != t2[i]) return 0;
}
return 1;
}
small int isSetOfAnagramas(char **strings, int *lengths, int setLen) {
int i, areAnagrams;
int *firstCharTable, *otherCharTable;
if (setLen <= 1) return 1;
firstCharTable = charFrequencyTable(strings[0], lengths[0]);
areAnagrams = 1;
for (i = 1; i < setLen; i++) {
otherCharTab = charFrequencyTable(strings[i], lengths[i]);
if (! equalCharTables(firstCharTable, otherCharTab) {
areAnagrams = 0;
break;
}
}
free(firstCharTable);
free(otherCharTab);
return areAnagrams;
}
As for the time complexity:
- you have to go through all strings => n strings
- for each string n_i, you have to go through all characters => len(n_i)
- you have to compare first string with all others => n - 1 comparisons
- for each comparison, you have to go through frequency table => len(alphabet)
So crunching those numbers, you get O(n * len(n_i) + (n - 1) * len(alphabet)).
Lengths of words and alphabet might not be larger than a certain valie, but they do add up to the complexity.
Ruby O(n) solution with O(n) extra space. First attempt overlooked the opportunity to return true as soon as the first anagram is encountered.
require 'set'
def anagrams(array)
set = Set.new
for word in array
sorted = word.chars.sort.join
if set.include? sorted
return true
else
set << sorted
end
end
false
end
puts anagrams(["bag","bat","tab"]) # true
puts anagrams(["gab","bat","laf"]) # false
Here is another solution in Objective C running in O(N)
char * toChar(NSString* str){
const char* strUtf8 = [str UTF8String];
size_t len = strlen(strUtf8) + 1;
char *toChar = malloc(len);
memcpy(toChar, strUtf8, len);
return toChar;
}
NSString* stringReverse(NSString* str){
char *cWord = toChar(str);
size_t len = strlen(cWord);
int start = 0;
int end = (int)len-1;
while (start<end) {
cWord[start] ^= cWord[end];
cWord[end] ^= cWord[start];
cWord[start] ^= cWord[end];
start++;
end--;
}
char reverseData[len];
memcpy(reverseData, cWord, len);
NSString *reverse = [[NSString alloc] initWithBytes:reverseData
length:sizeof(reverseData)
encoding:NSUTF8StringEncoding];
return reverse;
}
BOOL hasAnagram(NSArray * array){
NSMutableSet *set = [NSMutableSet new];
for(int i=0; i<array.count; i++){
if([set containsObject:array[i]])
return YES;
else
[set addObject:stringReverse(array[i])];
}
return NO;
}
int main(int argc, char *argv[]) {
@autoreleasepool {
NSArray *ar = @[@"bag", @"bat", @"tab"];
NSLog(@"Has anagram: %@", (hasAnagram(ar))?@"true":@"false");
ar = @[@"gab", @"bat", @"laf"];
NSLog(@"Has anagram: %@", (hasAnagram(ar))?@"true":@"false");
}
}
@interface AnagramTest : NSObject
- (BOOL)containsAnagrams:(NSArray*)wordSet;
- (NSMutableArray*)arrayFromString:(NSString*)myString;
@end
@implementation AnagramTest
- (BOOL)containsAnagrams:(NSArray*)wordSet{
NSMutableArray *sets = [[NSMutableArray alloc] init];
for(NSString *word in wordSet){
NSArray *wordArray = [self arrayFromString:word];
wordArray = [wordArray sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
if(![sets containsObject:wordArray]){
[sets addObject:wordArray];
}
else{
return YES;
}
}
return NO;
}
- (NSMutableArray*)arrayFromString:(NSString*)myString{
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[myString length]];
for (int i=0; i < [myString length]; i++) {
NSString *ichar = [NSString stringWithFormat:@"%c", [myString characterAtIndex:i]];
[characters addObject:ichar];
}
return characters;
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
AnagramTest *test = [[AnagramTest alloc] init];
NSArray *words = @[@"bad",@"tab",@"bat"];
NSLog(@"Set %@ anagrams",[test containsAnagrams:words] ? @"contains":@"doesn't contain");;
}
}
@interface AnagramTest : NSObject
- (BOOL)containsAnagrams:(NSArray*)wordSet;
- (NSMutableArray*)arrayFromString:(NSString*)myString;
@end
@implementation AnagramTest
- (BOOL)containsAnagrams:(NSArray*)wordSet{
NSMutableArray *sets = [[NSMutableArray alloc] init];
for(NSString *word in wordSet){
NSArray *wordArray = [self arrayFromString:word];
wordArray = [wordArray sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
if(![sets containsObject:wordArray]){
[sets addObject:wordArray];
}
else{
return YES;
}
}
return NO;
}
- (NSMutableArray*)arrayFromString:(NSString*)myString{
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[myString length]];
for (int i=0; i < [myString length]; i++) {
NSString *ichar = [NSString stringWithFormat:@"%c", [myString characterAtIndex:i]];
[characters addObject:ichar];
}
return characters;
}
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
AnagramTest *test = [[AnagramTest alloc] init];
NSArray *words = @[@"bad",@"tab",@"bat"];
NSLog(@"Set %@ anagrams",[test containsAnagrams:words] ? @"contains":@"doesn't contain");;
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
public class CostumCompare implements Comparator<String>{
public static String SortedString(String s){
char [] sa = s.toCharArray();
Arrays.sort(sa);
return new String(sa);
}
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return this.SortedString(o1).compareTo(this.SortedString(o2));
}
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
String [] input = {"test","etst","test2","tesst","test"};
for(String s: input){
String sorted = CostumCompare.SortedString(s);
if(hm.containsKey(sorted)){
hm.get(sorted).add(s);
}else{
hm.put(sorted, new ArrayList<String>());
hm.get(sorted).add(s);
}
}
for(String a:hm.keySet()){
System.out.println(hm.get(a));
}
}
Generate a identity for each word based on counted bit vector.
#import <Foundation/Foundation.h>
@interface BitVectorWithOccurences : NSObject <NSCopying>
@property (nonatomic) NSUInteger count;
- (void)setBitVectorByIndex:(CFIndex)index;
- (UInt8)getSetCountByIndex:(CFIndex)index;
- (void)reset;
@end
#import "BitVectorWithOccurences.h"
static const NSUInteger kBitVectorWithOccurencesCountDefault = 26;
static const NSUInteger kBitsPerByte = 8;
@interface BitVectorWithOccurences ()
@property (nonatomic) CFMutableBitVectorRef bitVector;
@end
@implementation BitVectorWithOccurences
- (instancetype)init {
if (self = [super init]) {
_bitVector = CFBitVectorCreateMutable(NULL, 0);
self.count = kBitVectorWithOccurencesCountDefault;
}
return self;
}
- (void)setCount:(NSUInteger)count {
if (_count != count) {
_count = count;
[self reset];
}
}
- (void)reset {
CFIndex bitsPerUnit = sizeof(UInt8) * kBitsPerByte;
CFBitVectorSetCount(_bitVector, bitsPerUnit * _count);
CFBitVectorSetAllBits(_bitVector, 0);
}
- (void)setBitVectorByIndex:(CFIndex)index {
[self addCountAtIndex:index countForSetBits:0];
}
- (UInt8)getSetCountByIndex:(CFIndex)index {
CFIndex bitsPerUnit = sizeof(UInt8) * kBitsPerByte;
UInt8 count = 0;
CFBitVectorGetBits(_bitVector, CFRangeMake(index * bitsPerUnit, bitsPerUnit), &count);
return count;
}
- (void)addCountAtIndex:(CFIndex)index countForSetBits:(NSUInteger)count {
CFIndex bitsPerUnit = sizeof(UInt8) * kBitsPerByte;
if (count < bitsPerUnit) {
CFBit bit = CFBitVectorGetBitAtIndex(_bitVector, (index + 1) * bitsPerUnit - count - 1);
CFBit valueToSet = 1 ^ bit;
CFBitVectorSetBitAtIndex(_bitVector, (index + 1) * bitsPerUnit - count - 1, valueToSet);
if (valueToSet == 0) {
[self addCountAtIndex:index countForSetBits:count + 1];
}
} else {
[NSException raise:@"" format:@"Set index so many times!"];
}
}
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[self class]]) {
BitVectorWithOccurences *another = (BitVectorWithOccurences *)object;
if (_count == another.count) {
UInt8 selfByte = 0, anotherByte = 0;
for (NSInteger idx = 0; idx < _count; idx++) {
CFIndex bitsPerUnit = sizeof(UInt8) * kBitsPerByte;
CFBitVectorGetBits(_bitVector, CFRangeMake(idx * bitsPerUnit, bitsPerUnit), &selfByte);
CFBitVectorGetBits(another->_bitVector, CFRangeMake(idx * bitsPerUnit, bitsPerUnit), &anotherByte);
if (selfByte != anotherByte) {
return NO;
}
}
return YES;
}
}
return NO;
}
- (NSString *)description {
NSMutableString *bits = [NSMutableString stringWithString:@""];
NSUInteger countUnits = 0;
NSUInteger bitsPerUnit = sizeof(UInt8) * kBitsPerByte;
for (CFIndex i = 0; i < CFBitVectorGetCount(_bitVector); i++) {
CFBit bit = CFBitVectorGetBitAtIndex(_bitVector, i);
if (bit == 1) {
[bits appendString:@"1"];
} else {
[bits appendString:@"0"];
}
countUnits++;
if ((countUnits % bitsPerUnit) == 0) {
[bits appendString:@"|"];
}
}
return [bits copy];
}
- (id)copyWithZone:(nullable NSZone *)zone {
BitVectorWithOccurences *bitVector = [BitVectorWithOccurences new];
bitVector.count = _count;
bitVector->_bitVector = CFBitVectorCreateMutableCopy(NULL, 0, _bitVector);
return bitVector;
}
@end
void main() {
NSArray *wordArray1 = @[@"bag", @"bat", @"tab"];
NSArray *wordArray2 = @[@"gab", @"bat", @"laf"];
NSLog(@"%d", [self containsAnagrams:wordArray1]);
NSLog(@"%d", [self containsAnagrams:wordArray2]);
}
- (BOOL)containsAnagrams:(NSArray *)wordArray
{
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:wordArray.count];
__block BOOL found = NO;
[wordArray enumerateObjectsUsingBlock:^(NSString * _Nonnull word, NSUInteger idx, BOOL * _Nonnull stop) {
BitVectorWithOccurences *bitVector = [self wordIdentity:word];
NSArray *array = [dict objectForKey:bitVector];
if (array.count) {
found = YES;
*stop = YES;
} else {
[dict setObject:[NSMutableArray arrayWithObject:word] forKey:bitVector];
}
}];
return found;
}
-(void)findAnagram
{
NSArray *valuesList =[[NSArray alloc]initWithObjects:@"bat",@"cat",@"sdf",@"tab", nil];
NSMutableArray *countList =[[NSMutableArray alloc]init];
for (int k=0; k<valuesList.count; k++) {
NSString *string = [valuesList objectAtIndex:k];
int total = 0;
for(int i=0;i<string.length;i++)
{
NSString *singleChar = [string substringWithRange:NSMakeRange(i, 1)];
int charValue = [singleChar characterAtIndex:0];
total = total + charValue;
}
if ([countList containsObject:[NSString stringWithFormat:@"%d",total]]) {
NSLog(@"Found");
break;
}
else
{
[countList addObject:[NSString stringWithFormat:@"%d",total]];
}
}
}
I used a custom hash table that would return false if the hash of a sorted string already existed in the hash table.
+(void)test {
NSLog(@"Anagrams in array?: %hhd", [self anagramsInArray:@[@"cat", @"cta", @"dog"]]);
}
+(BOOL)anagramsInArray:(NSArray*)array {
if (array.count < 1 || array == nil) {
return false;
}
HashTable *hashTable = [HashTable new];
for (id possibleString in array) {
if ([possibleString isKindOfClass:[NSString class]]) {
NSString *str = (NSString*)possibleString;
if (![hashTable tryAddingUniqueValue:[str sortAscending] ForKey:[str sortAscending]]) {
return true;
}
}
}
return false;
}
+(NSArray*)stringToArray:(NSString*)string {
NSMutableArray *mutableStringArray = [NSMutableArray new];
for (int i = 0; i < [string length]; i++ ) {
[mutableStringArray addObject:[NSString stringWithFormat:@"%c",[string characterAtIndex:i]]];
}
return mutableStringArray.copy;
}
This is about as small as I could crunch this. Other answers along the way allude to portions of the solution here.
/**
* @brief Determine if two words are anagrams of each other.
* @discussion Runtime O( 2n )
*
* @param word1 The first word
* @param word2 The second word. Possibly an anagram.
*
* @return YES if the two words are anagrams, NO otherwise.
*/
bool areAnagrams(NSString *word1, NSString *word2)
{
if (word1.length != word2.length || word1.length == 0 || word2.length == 0) {
return NO;
}
const int size = 128;
NSInteger len = [word1 length];
NSInteger counter1[size];
NSInteger counter2[size];
NSInteger i;
//This only shows values in LLDB if the arrays are statically sized during compilation.
// For performance, these arrays should be initialized in their own for loop; stepping
// through each array index.
memset(counter1, 0, sizeof(counter1));
memset(counter2, 0, sizeof(counter2));
for (i = 0; i < len; i++) {
counter1[[word1 characterAtIndex:i]]++;
counter2[[word2 characterAtIndex:i]]++;
}
for (i = 0; i < size; i++) {
if (counter1[i] != counter2[i]) {
return FALSE;
}
}
return YES;
}
Using hash table:
You can loop on all words
for each word loop on its characters one by one as substring and sum their hashValue.
Once you have the hash value of all characters of the word, check if they are exist in the hash table, if YES, then we found a palindrome (there should be a dictionary to say its anagram), if NO, add this hash value to the hash table
This has linear space and time complexity of N characters of all words.
Below is the code that runs in O(n) written in Python. Running : ideone.com/4VksgA
from collections import Counter
strings = [['bag', 'bat', 'tab'], ["gab", "bat", "laf"]]
def twoAnagrams(strings):
frequencies = set([])
present = False
for str in strings:
freq = frozenset(Counter(str).items())
if freq in frequencies:
present = True
break
else:
frequencies.add(freq)
print ("TRUE" if present else "FALSE")
for s in strings:
twoAnagrams(s)
Yes the solution looks good. But what is the hidden time complexity of these operations like "frozenset(Counter(str).items())" and "freq in frequencies". Especially the worst case time complexity? I guess freq is an unordered set so the time complexity is generally more than comparing two ordered sets.
For a single word W, we need
* O(|W|) for counting the letters
* At most O(|W|) for converting it into set (perhaps not needed in other languages, in Python, we can't hash a dictionary)
* O(|W|) on average for looking it up in the set - I think it's safe to assume that we've got a sane implementation of hashtable
Note that set('a', 'b', 'c') is by design equal to set('c', 'b', 'a') and so it holds for the hash codes, which is the only thing that interests us. Internally, frozenset doesn't sort the items anyhow.
For all words, it just sums up to the size of the input.
For each word in the array, sort it and then store it in a hash table. As soon as you encounter a word whose sorted version exists in the hash table, that means we have found a anagram.
- prd April 23, 2014