4

我正在制作一个 ios 应用程序,您可以在其中输入 9 个字母,它将输出这 9 个字母的字谜。它就像目标词,或论文中的 9 个字母词。喜欢这个链接:

http://nineletterword.tompaton.com/

它不只是为 9 个字母提供字谜,它会为 4 个字母、5 个字母、6 个字母......所有这些都至少包含中间字母。

我想让它成为一个离线应用程序,所以我不想引用任何网站或使用在线 json...

我将如何检查是否可以将 9 个字母的数组重新排列成我下载的英语词典中的单词。

例如,我有 (a,b,a,n, D ,o,n,e,d) 的输入:如何在一个名为“英语词典”的数组中获得 4 个或更多的英语单词的输出必须包含中间字母“D”——如“abandon”、“bond”、“dead”...

最好的方法是很多很多循环和 if 语句,还是 xcode/objective c 中有一些东西我可以用来获取 4 个字母的列表,然后对其进行所有可能的安排......

干杯

4

2 回答 2

3

让我提出一个不同的算法,它依赖于查找,而不是通过数组进行搜索。

设置:

遍历字典中的单词。对于每个单词,创建一个具有相同字符的字符串,按字母顺序排序。使用此字符串作为键,创建原始单词数组的字典。

用法:

现在您可以非常快速地检查任何字符组合:只需像上面那样对字符进行排序,然后在地图中查找生成的键。

例子:

原始数组:( bond, Mary, army )

字谜查找图:

{
   bdno : ( bond ),
   amry : ( Mary, army ),
}

使用此地图可以非常快速地检查任何单词的字谜。不需要对字典数组进行迭代。

编辑:

我提出的算法分为三个部分:

  1. 从对象字典构建查找映射的设置方法:anagramMap
  2. 一种计算逐字符排序键的方法:anagramKey
  3. 一种算法,查找包含在九个字母单词中的字符的所有排列,并在地图中查找单词:findAnagrams.

这是所有三种方法的实现,作为 上的一个类别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);
于 2013-04-08T10:12:06.450 回答
2

首先,您需要一种方法来检查一个单词是否是第二个单词的字谜。有许多可能的解决方案(搜索“Objective-C anagram”)。这本质上是来自 https://stackoverflow.com/a/13465672/1187415的方法,写法略有不同:

- (BOOL)does:(NSString*)longWord contain:(NSString *)shortWord
{
    NSMutableString *longer = [longWord mutableCopy];
    __block BOOL retVal = YES;
    // Loop over all characters (letters) in shortWord:
    [shortWord enumerateSubstringsInRange:NSMakeRange(0, [shortWord length])
                                  options:NSStringEnumerationByComposedCharacterSequences
                               usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        // Check if letter occurs in longer word:
        NSRange letterRange = [longer rangeOfString:substring];
        if (letterRange.location != NSNotFound) {
            // Yes. Remove from longer word and continue.
            [longer deleteCharactersInRange:letterRange];
        } else {
            // No. Set return value to NO and quit the loop.
            retVal = NO;
            *stop = YES;
        }
    }];
    return retVal;
}

例子:

  • [self does:@"abandoned" contain:@"bond"] = YES
  • [self does:@"abandoned" contain:@"sea"] = NO,因为第一个单词中没有“s”。
  • [self does:@"abandoned" contain:@"noon"] = NO,因为“noon”有2个字母“o”,但第一个单词只有一个“o”。

然后,您可以按以下方式进行:

NSArray *englishWords = ...; // Your array of english words

NSString *inputWord = @"abandoned"; // The input string
NSString *middleLetter = [inputWord substringWithRange:NSMakeRange([inputWord length]/2, 1)];

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *word, NSDictionary *bindings) {
    // Word must have at least 4 letters:
    if ([word length] < 4)
        return NO;
    // Word must contain the middle letter:
    if ([word rangeOfString:middleLetter].location == NSNotFound)
        return NO;
    // Word must contain only letters of the input word:
    if (![self does:inputWord contain:word])
        return NO;
    return YES;
}];
NSArray *matchingWords = [englishWords filteredArrayUsingPredicate:predicate];
NSLog(@"%@", matchingWords);
于 2013-04-07T06:31:05.667 回答