views:

73

answers:

3

Hi All,

Here's what I want to do. I have 2 strings and I want to determine if one string is a permutation of another. I was thinking to simply remove the characters from string A from string B to determine if any characters are left. If no, then it passes.

However, I need to make sure that only 1 instance of each letter is removed (not all occurrences) unless there are multiple letters in the word.

An example:

String A: cant

String B: connect

Result: -o-nec-

Experimenting with NSString and NSScanner has yielded no results so far.

A: 

Hmmm, let's have a go:

NSString *stringA = @"cant";
NSString *stringB = @"connect";

NSUInteger length = [stringB length];
NSMutableCharacterSet *charsToRemove = [NSMutableCharacterSet characterSetWithCharactersInString:stringA];

unichar *buffer = calloc(length, sizeof(unichar));
[stringB getCharacters:buffer range:NSMakeRange(0, length)];

for (NSUInteger i = 0; i < length; i++)
{
    if ([charsToRemove characterIsMember:buffer[i]])
    {
        [charsToRemove removeCharactersInRange:NSMakeRange(buffer[i], 1)];
        buffer[i] = '-';
    }
}

NSString *result = [NSString stringWithCharacters:buffer length:length];

free (buffer);
dreamlax
Sorry, just saw stringA and stringB and result, didn't fully read the question ;)
dreamlax
A: 

An inefficient yet simple way might be something like this (this is implemented as a category on NSString, but it could just as easily be a method or function taking two strings):

@implementation NSString(permutation)
- (BOOL)isPermutation:(NSString*)other
{
    if( [self length] != [other length] ) return NO;
    if( [self isEqualToString:other] )    return YES;
    NSUInteger length = [self length];
    NSCountedSet* set1 = [[[NSCountedSet alloc] initWithCapacity:length] autorelease];
    NSCountedSet* set2 = [[[NSCountedSet alloc] initWithCapacity:length] autorelease];
    for( int i = 0; i < length; i++ ) {
        NSRange range = NSMakeRange(i, 1);
        [set1 addObject:[self substringWithRange:range]];
        [set2 addObject:[self substringWithRange:range]];
    }
    return [set1 isEqualTo:set2];
}
@end
Jason Coco
This solution presumes that both the words are the same length which won't work in this case. I need to be able to check for subsets of a larger word as well. Sorry if I wasn't clear :(
Luke
@Luke: You can easily just remove the length bits and use the larger of the two strings for the length. From your question, I thought you wanted to check if one string were a permutation of another string, which would be false if the strings were different lengths.
Jason Coco
@Jason Coco: Yeah. That was my bad. Sorry for the mixup. I mean't is the word a subset or permutation of the word, meaning given the letters in word B, could I make A without repeating. Will try to be more clear next time. Thanks for your help though!
Luke
A: 

This returns what your example asks for...

NSString* a = @"cant";
    NSString* b = @"connect";

    NSMutableString* mb = [NSMutableString stringWithString:b];
    NSUInteger i;
    for (i=0; i<[a length]; i++) {
        NSString* theLetter = [a substringWithRange:NSMakeRange(i, 1)];
        NSRange r = [mb rangeOfString:theLetter];
        if (r.location != NSNotFound) {
            [mb replaceCharactersInRange:r withString:@"-"];
        }
    }
    NSLog(@"mb: %@", mb);

However, I wouldn't call that a permutation. To me a permutation would only hold true if all the characters from string "a" were contained by string "b". In your example, since the letter a in cant isn't in string b then I would say that cant is not a permutation of connect. With this definition I would use this:

-(BOOL)isString:(NSString*)firstString aPermutationOfString:(NSString*)secondString {
    BOOL isPermutation = YES;
    NSMutableString* mb = [NSMutableString stringWithString:secondString];
    NSUInteger i;
    for (i=0; i<[firstString length]; i++) {
        NSString* theLetter = [firstString substringWithRange:NSMakeRange(i, 1)];
        NSRange r = [mb rangeOfString:theLetter];
        if (r.location != NSNotFound) {
            [mb deleteCharactersInRange:r];
        } else {
            return NO;
        }

    }
    return isPermutation;
}
regulus6633
True. I guess what I was looking for was derivative or subsets of a larger character set. Having said this, I am marking this answer as correct as it provides both functions. I also tested all of the code samples provided and they work fine. As a side note, I had working code that checked char values and moved the memory block but kept finding that I ran out of memory, even though it didn't seem to be leaking. The solution, or problem, was how I had the NSAutoreleasePool set up. So if anyone uses this code in batch operations, probably check that :)Thanks to everyone!!
Luke