Перестановки / анаграммы в Objective-C - я что-то упустил - PullRequest
7 голосов
/ 08 июля 2011

(код ниже относительно моего вопроса)

За этот вопрос о переполнении стека Я использовал подход Пеголона для генерации всех возможных перестановок группы символов внутри строки NSString.Однако сейчас я пытаюсь заставить его не просто генерировать ANAGRAM, который представляет собой все перестановки одинаковой длины, но и все возможные комбинации (любой длины) символов в строке.

Кто-нибудь знает, как яизменит следующий код, чтобы заставить это сделать это?Это очень похоже на: Генерация всех перестановок любой длины - но (из-за страха, что им нужен ответ на домашнюю работу) они не оставили код.У меня есть пример того, что я думал сделать в нижней части этого поста ... но это не так.

Итак, код, как есть, генерирует the, teh, hte, het, eth и eht при получении THE.Что мне нужно, так это: t, h, e, th, ht, te, he (и т. Д.) В дополнение к вышеуказанным 3 комбинациям символов.

Как бы я изменил это, пожалуйста.(ps: в этом есть два метода. Я добавил allPermutationsArrayofStrings, чтобы вернуть результаты в виде строк, как я хочу, а не просто массив символов в другом массиве).Я предполагаю, что магия случится в pc_next_permutation в любом случае - но думал, что упомяну это.

В NSArray + Permutation.h

#import <Foundation/Foundation.h>

@interface NSArray(Permutation)
- (NSArray *)allPermutationsArrayofArrays;
- (NSArray *)allPermutationsArrayofStrings;

@end

в NSArray + Permutation.m:

#define MAX_PERMUTATION_COUNT   20000

NSInteger *pc_next_permutation(NSInteger *perm, const NSInteger size);
NSInteger *pc_next_permutation(NSInteger *perm, const NSInteger size) 
{
    // slide down the array looking for where we're smaller than the next guy
    NSInteger pos1;
    for (pos1 = size - 1; perm[pos1] >= perm[pos1 + 1] && pos1 > -1; --pos1);

    // if this doesn't occur, we've finished our permutations
    // the array is reversed: (1, 2, 3, 4) => (4, 3, 2, 1)
    if (pos1 == -1)
        return NULL;

    assert(pos1 >= 0 && pos1 <= size);

    NSInteger pos2;
    // slide down the array looking for a bigger number than what we found before
    for (pos2 = size; perm[pos2] <= perm[pos1] && pos2 > 0; --pos2);

    assert(pos2 >= 0 && pos2 <= size);

    // swap them
    NSInteger tmp = perm[pos1]; perm[pos1] = perm[pos2]; perm[pos2] = tmp;

    // now reverse the elements in between by swapping the ends
    for (++pos1, pos2 = size; pos1 < pos2; ++pos1, --pos2) {
        assert(pos1 >= 0 && pos1 <= size);
        assert(pos2 >= 0 && pos2 <= size);

        tmp = perm[pos1]; perm[pos1] = perm[pos2]; perm[pos2] = tmp;
    }

    return perm;
}

@implementation NSArray(Permutation)

- (NSArray *)allPermutationsArrayofArrays
{
    NSInteger size = [self count];
    NSInteger *perm = malloc(size * sizeof(NSInteger));

    for (NSInteger idx = 0; idx < size; ++idx)
        perm[idx] = idx;

    NSInteger permutationCount = 0;

    --size;

    NSMutableArray *perms = [NSMutableArray array];

    do {
        NSMutableArray *newPerm = [NSMutableArray array];

        for (NSInteger i = 0; i <= size; ++i)
            [newPerm addObject:[self objectAtIndex:perm[i]]];

        [perms addObject:newPerm];
    } while ((perm = pc_next_permutation(perm, size)) && ++permutationCount < MAX_PERMUTATION_COUNT);
    free(perm);

    return perms;
}

- (NSArray *)allPermutationsArrayofStrings
{
    NSInteger size = [self count];
    NSInteger *perm = malloc(size * sizeof(NSInteger));

    for (NSInteger idx = 0; idx < size; ++idx)
        perm[idx] = idx;

    NSInteger permutationCount = 0;

    --size;

    NSMutableArray *perms = [NSMutableArray array];

    do {
        NSMutableString *newPerm = [[[NSMutableString alloc]initWithString:@"" ]autorelease];

        for (NSInteger i = 0; i <= size; ++i)
        {
            [newPerm appendString:[self objectAtIndex:perm[i]]];
        }
        [perms addObject:newPerm];
    } while ((perm = pc_next_permutation(perm, size)) && ++permutationCount < MAX_PERMUTATION_COUNT);
    free(perm);

    return perms;
}

@end

Мой код, который я думал, исправит это:

for ( NSInteger i = 1; i <= theCount; i++) {
                NSRange theRange2;
                theRange2.location = 0;
                theRange2.length = i;
                NSLog(@"Location: %i (len: %i) is: '%@'",theRange2.location,theRange2.length,[array subarrayWithRange:theRange2]);

                NSArray *allWordsForThisLength = [[array subarrayWithRange:theRange2] allPermutationsArrayofStrings];
                for (NSMutableString *theString in allWordsForThisLength)
                {
                    NSLog(@"Adding %@ as a possible word",theString);
                    [allWords addObject:theString];
                }

Я знаю, что это будет не самым эффективным ... но я пытался проверить.

Вот что я получил:

2011-07-07 14:02:19.684 TA[63623:207] Total letters in word: 3
2011-07-07 14:02:19.685 TA[63623:207] Location: 0 (len: 1) is: '(
    t
)'
2011-07-07 14:02:19.685 TA[63623:207] Adding t as a possible word
2011-07-07 14:02:19.686 TA[63623:207] Location: 0 (len: 2) is: '(
    t,
    h
)'
2011-07-07 14:02:19.686 TA[63623:207] Adding th as a possible word
2011-07-07 14:02:19.687 TA[63623:207] Adding ht as a possible word
2011-07-07 14:02:19.688 TA[63623:207] Location: 0 (len: 3) is: '(
    t,
    h,
    e
)'
2011-07-07 14:02:19.688 TA[63623:207] Adding the as a possible word
2011-07-07 14:02:19.689 TA[63623:207] Adding teh as a possible word
2011-07-07 14:02:19.690 TA[63623:207] Adding hte as a possible word
2011-07-07 14:02:19.691 TA[63623:207] Adding het as a possible word
2011-07-07 14:02:19.691 TA[63623:207] Adding eth as a possible word
2011-07-07 14:02:19.692 TA[63623:207] Adding eht as a possible word

Как видите, ни одного, ни двух буквенных слов - я выдергиваю волосы!(и у меня не так много свободного!)

Ответы [ 2 ]

2 голосов
/ 08 июля 2011

Хорошо,

Пока плохо и грязно, однако, это то, что я сделал ...

Я изменил NSArray+Permutations.m следующим образом:

- (NSArray *)allPermutationsArrayofStrings
{
    NSInteger size = [self count];
    NSInteger *perm = malloc(size * sizeof(NSInteger));

    for (NSInteger idx = 0; idx < size; ++idx)
        perm[idx] = idx;

    NSInteger permutationCount = 0;

    --size;

    NSMutableArray *perms = [NSMutableArray array];
    NSMutableDictionary *permsDict = [[NSMutableDictionary alloc] init];

    do {
        NSMutableString *newPerm = [[[NSMutableString alloc]initWithString:@"" ]autorelease];

        for (NSInteger i = 0; i <= size; ++i)
        {
            [newPerm appendString:[self objectAtIndex:perm[i]]];
        }

        if ([permsDict objectForKey:newPerm] == nil)
        {
            [permsDict setObject:@"1" forKey:newPerm];
            [perms addObject:newPerm];
        }

        for (NSInteger i = 1; i <= [newPerm length]; ++i)
        {
            NSRange theRange;
            theRange.location = 0;
            theRange.length = i;
            if ([permsDict objectForKey:[newPerm substringToIndex:i]] ==  nil)
            {
                [permsDict setObject:@"1" forKey:[newPerm substringToIndex:i]];
                [perms addObject:[newPerm substringToIndex:i]];
            }
        }

    } while ((perm = pc_next_permutation(perm, size)) && ++permutationCount < MAX_PERMUTATION_COUNT);
    free(perm);

    [permsDict release];

    return perms;
}

Основными изменениями была идея @PengOne ... Возвращать полученную в результате лексически измененную строку, а также сокращать ее на 1 символ за раз и добавлять ее в возвращаемый массив, если он еще не существовал.

Я решил быть "дешевым" в этом вопросе и отслеживать, используя NSMutableDictionary. Поэтому, если лексически измененная строка не была указана в словаре, она была добавлена.

Это более или менее то, что, как ты думал, я должен делать, @PengOne?

ПУТЬ быстрее, чем добавлять их все и обрабатывать получающиеся дубликаты позже - и я думаю, что это работает так, как мне нужно.

2 голосов
/ 08 июля 2011

Легко сделать, чтобы взять все подмножества размера k и использовать код, который вам нужен для генерации всех перестановок подмножества. Это легко, но не самый эффективный.


Вот лучший подход. Вы генерируете перестановки лексикографически в первой процедуре:

1234
1243
1324
1342
1423
...

Каждый раз, когда вы звоните NSInteger *pc_next_permutation(NSInteger *perm, const NSInteger size), вы получаете следующую перестановку в лексическом порядке, находя правильную позицию для изменения. Когда вы сделаете это, обрежьте место, которое вы изменили, чтобы получить следующее:

1234 123 12 1
1243 124
1324 132 13
1342 134
1423 142 14
1432 143
2143 214 21 2
...

Надеюсь, идея понятна. Вот один из способов реализовать это (в псевдокоде типа Objective C).

-(NSMutableArray *)nextPerms:(Perm *)word {
    int N = word.length;
    for (int i=N-1; i > 0; ++i) {
        if (word[i-1] < word[i]) {
            break;
        } else if (i==1) {
            i = 0;
        }
    }
    // At this point, i-1 is the leftmost position that will change
    if (i == 0) {
        return nil;
    }
    i = i-1;
    // At this point, i is the leftmost position that will change
    Perm *nextWord = word;
    for (int j=1; j <= N-i; ++j) {
        nextWord[i+j] = word[N-j];
    }
    nextWord[i] = nextWord[i+1];
    nextWord[i+1] = word[i];

    // At this point, nextPerm is the next permutation in lexicographic order.    

    NSMutableArray *permList = [[NSMutableArray alloc] init];
    for (int j=i; j<N; ++j) {
        [permList addObject:[nextWord subwordWithRange:NSMakeRange(0,i)]];
    }
    return [permList autorelease];
}

Это вернет массив с частичными перестановками, как описано выше. Вход для nextPerms должен быть lastObject от выхода nextPerms.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...