重新排列数组中的字母并检查排列是不是在数组中

Posted

技术标签:

【中文标题】重新排列数组中的字母并检查排列是不是在数组中【英文标题】:Rearrange Letters from Array and check if arrangement is in array重新排列数组中的字母并检查排列是否在数组中 【发布时间】:2013-03-29 08:42:02 【问题描述】:

我正在制作一个 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 个字母的列表,然后对其进行所有可能的排列...

干杯

【问题讨论】:

我面临同样的问题......但我没有从已回答的算法中得到所有可能的字谜。 【参考方案1】:

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

设置:

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

用法:

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

例子:

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

字谜查找图:


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

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

编辑:

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

    从对象字典构建查找映射的设置方法:anagramMap 一种按字符计算排序键的方法:anagramKey 一种算法,查找九个字母单词中包含的字符的所有排列并在映射中查找单词: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);

【讨论】:

这是个好主意!但我唯一的问题是我必须想办法重新排列 9 个字母单词中的字母。例如。我如何从“被遗弃”到“bdno”?如果我能为此做点什么的话,那会很管用!谢谢 @falky 我添加了一个迭代所有可能的字符组合的方法。请参阅findAnagrams 实现。 非常感谢您编写此方法。我将在今天下午晚些时候尝试实施它。这似乎比遍历字典要容易得多,而且速度也更快! 我尝试实现它,但我不知道如何实现。我已将您的代码放入已导入主视图控制器文件的新类别中。我有一个来自您在应用程序上按下的按钮的操作方法。我在里面放了什么,然后只是简单地 nslog 字谜列表?比如我应该输入什么?我在一个名为 Nineletters 的 nsstring 中有 9 个字母。抱歉,我是新手,所以不太明白。感谢您的帮助! 我可能错了,但在我看来,算法检查该单词是否包含输入单词的 anagramKey 的中间字母,而不是输入词。【参考方案2】:

首先,您需要一种方法来检查一个单词是否是第二个单词的字谜。有许多可能的解决方案(搜索“Objective-C anagram”)。这基本上是来自的方法 https://***.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);

【讨论】:

但这是否允许我通过更大的单词中单词的所有可能性...我不太了解您编码的第一部分...这基本上会通过所有可能性更大的 9 个字母单词中的单词?非常感谢您的帮助! @falky:我在代码中添加了一些 cmets 和示例。如果我正确理解你的问题,这应该完全符合你的需要。你为什么不试试看它是否适合你? - 当然,如果需要,欢迎您提出更多问题! 哦好的!!!现在我明白了。这更有意义。这实际上是一个非常好的主意!因此,您基本上是在检查字典中的单词是否在 9 个字母单词中。很聪明!非常感谢您对这个问题和其他问题的帮助。我现在可以投票了!今晚晚些时候我会测试一下! 想知道您是否可以提供帮助:***.com/questions/15877151/…

以上是关于重新排列数组中的字母并检查排列是不是在数组中的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 937. 重新排列日志文件 / 1823. 找出游戏的获胜者(约瑟夫环问题) / 713. 乘积小于 K 的子数组

1451. 重新排列句子中的单词

如何重新排列数组中的数据,使两个相似的项目不相邻?

PHP - 获取 URL 参数并在数组中重新排列

Java - 检查字符串的 ArrayList 是不是按字母顺序排列

检查一个字符串的字母在另一个字符串中是不是按顺序排列