配列の検索ではなく、ルックアップに依存する別のアルゴリズムを提案させてください。
設定:
辞書内の単語を反復処理します。単語ごとに、同じ文字をアルファベット順に並べた文字列を作成します。この文字列をキーとして、元の単語の配列の辞書を作成します。
使用法:
これで、任意の文字の組み合わせのチェックを非常に迅速に行うことができます。上記のように文字を並べ替えて、結果のキーをマップで調べるだけです。
例:
元の配列:( bond, Mary, army )
アナグラム ルックアップ マップ:
{
bdno : ( bond ),
amry : ( Mary, army ),
}
このマップを使用すると、単語のアナグラムをすばやく確認できます。ディクショナリ配列の反復は必要ありません。
編集:
私が提案したアルゴリズムは、次の 3 つの部分に分かれています。
- オブジェクトのディクショナリからルックアップ マップを作成するセットアップ メソッド:
anagramMap
- 文字ごとにソートされたキーを計算する方法:
anagramKey
- 9 文字の単語に含まれる文字のすべての順列を検索し、マップ内の単語を検索するアルゴリズム:
findAnagrams
.
上のカテゴリとして 3 つのメソッドすべてを実装したものを次に示しますNSString
。
@interface NSString (NSStringAnagramAdditions)
- (NSSet *)findAnagrams;
@end
@implementation NSString (NSStringAnagramAdditions)
+ (NSDictionary *)anagramMap
{
static NSDictionary *anagramMap;
if (anagramMap != nil)
return anagramMap;
// this file is present on Mac OS and other unix variants
NSString *allWords = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
encoding:NSUTF8StringEncoding
error:NULL];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
@autoreleasepool {
[allWords enumerateLinesUsingBlock:^(NSString *word, BOOL *stop) {
NSString *key = [word anagramKey];
if (key == nil)
return;
NSMutableArray *keyWords = [map objectForKey:key];
if (keyWords == nil) {
keyWords = [NSMutableArray array];
[map setObject:keyWords forKey:key];
}
[keyWords addObject:word];
}];
}
anagramMap = map;
return anagramMap;
}
- (NSString *)anagramKey
{
NSString *lowercaseWord = [self lowercaseString];
// make sure to take the length *after* lowercase. it might change!
NSUInteger length = [lowercaseWord length];
// in this case we're only interested in anagrams 4 - 9 characters long
if (length < 4 || length > 9)
return nil;
unichar sortedWord[length];
[lowercaseWord getCharacters:sortedWord range:(NSRange){0, length}];
qsort_b(sortedWord, length, sizeof(unichar), ^int(const void *aPtr, const void *bPtr) {
int a = *(const unichar *)aPtr;
int b = *(const unichar *)bPtr;
return b - a;
});
return [NSString stringWithCharacters:sortedWord length:length];
}
- (NSSet *)findAnagrams
{
unichar nineCharacters[9];
NSString *anagramKey = [self anagramKey];
// make sure this word is not too long/short.
if (anagramKey == nil)
return nil;
[anagramKey getCharacters:nineCharacters range:(NSRange){0, 9}];
NSUInteger middleCharPos = [anagramKey rangeOfString:[self substringWithRange:(NSRange){4, 1}]].location;
NSMutableSet *anagrams = [NSMutableSet set];
// 0x1ff means first 9 bits set: one for each character
for (NSUInteger i = 0; i <= 0x1ff; i += 1) {
// skip permutations that do not contain the middle letter
if ((i & (1 << middleCharPos)) == 0)
continue;
NSUInteger length = 0;
unichar permutation[9];
for (int bit = 0; bit <= 9; bit += 1) {
if (i & (1 << bit)) {
permutation[length] = nineCharacters[bit];
length += 1;
}
}
if (length < 4)
continue;
NSString *permutationString = [NSString stringWithCharacters:permutation length:length];
NSArray *matchingAnagrams = [[self class] anagramMap][permutationString];
for (NSString *word in matchingAnagrams)
[anagrams addObject:word];
}
return anagrams;
}
@end
と呼ばれる変数にテスト文字列があると仮定すると、nineletters
次を使用して可能な値をログに記録します。
for (NSString *anagram in [nineletters findAnagrams])
NSLog(@"%@", anagram);