iOS-字符串查找

字符串查找通常有四种方式,暴力查找,KMP查找,BoyerMoore查找以及RabinKarp算法查找,查找最简单的莫过于暴力查找,如果内容是“FlyElephant”,需要查找的内容是“Elephant”,上面的四种方式会给出不同的方案~

暴力查找

最简单的暴力查找:

+(BOOL)simpleMatchPattern:(NSString *)text pattern:(NSString *)pattern{
   NSInteger cnlen=text.length;
   NSInteger ptlen=pattern.length;
   for (NSInteger i=0; i<=cnlen-ptlen; i++) {
       NSInteger j;
       for (j=0; j<ptlen; j++) {
           unichar tChar=[text characterAtIndex:i+j];
           unichar pChar=[pattern characterAtIndex:j];
           if (tChar!=pChar) {
               break;
           }
       }
       if (j==ptlen) {
           return YES;
       }
   }
   return NO;
}

遍历内容的长度,获取索引m,然后索引m...m+匹配长度,逐一比较,这个算法可以稍微改进一下:

+(NSInteger)simpleBackMatchPattern:(NSString *)text pattern:(NSString *)pattern{
   NSInteger i,j;
   NSInteger cnlen=text.length;
   NSInteger ptlen=pattern.length;
   for (i=0,j=0; i<cnlen && j<ptlen; i++) {
       unichar tChar=[text characterAtIndex:i];
       unichar pChar=[pattern characterAtIndex:j];
       if (tChar==pChar) {
           j++;
       }else{
           i=i-j;
           j=0;
       }
   }
   if (j==ptlen) {
       return i-ptlen;
   }else{
        return 0;
   }
}

KMP算法

KMP是knuth,Morris和Pratt发明的算法,暴力查找的每次只是循环是固定的加1,假设内容中只有A,B两种你字符,假设查找的字符串是BAAAABAAB,模式字符串为BAAB,我们发现前两个匹配,第三个不匹配,暴力中,我们的循环会从索引1开始继续比较,但是匹配我们知道其他的全是A,所以我们应该从第二个字母B开始匹配,减少回退的成本,实现代码:

static const NSInteger LetterCount=256;
@interface KMPSearch()

@property (strong,nonatomic) NSString *pattern;

@property (strong,nonatomic) NSMutableArray *dfa;

@end

@implementation KMPSearch

-(instancetype)initWithPattern:(NSString *)pattern{
   self=[super init];
   if (self) {
       self.pattern=pattern;
       [self setupData];
   }
   return self;
}

-(NSInteger)search:(NSString *)text{
   NSInteger i,j;
   NSInteger plen=self.pattern.length;
   NSInteger tlen=text.length;
   for (i=0,j=0; i<tlen & j<plen; i++) {
       unichar uc=[text characterAtIndex:i];
       NSArray *arr=self.dfa[uc];
       j=[arr[j] integerValue];
   }
   if (j==plen) {
       return i-plen;
   }else{
       return -1;
   }
}

-(void)setupData{
   NSInteger plen=self.pattern.length;

   self.dfa=[[NSMutableArray alloc]initWithCapacity:LetterCount];
   
   for (NSInteger i=0; i<LetterCount; i++) {
       NSMutableArray *arr=[[NSMutableArray alloc]init];
       for (NSInteger m=0; m<plen; m++) {
           [arr addObject:@(0)];
       }
       [self.dfa addObject:arr];
   }
   
   NSInteger index=[self.pattern characterAtIndex:0];
   self.dfa[index][0]=@(1);
   for (NSInteger X=0,j=1; j<plen;j++) {
       for (NSInteger c=0; c<LetterCount; c++) {
           self.dfa[c][j]=self.dfa[c][X];
       }
       NSInteger uc=[self.pattern characterAtIndex:j];
       self.dfa[uc][j]=@(j+1);
       X=[self.dfa[uc][X] integerValue];
   }
   
}

@end

BoyerMooer算法

之前的暴力查找和KMP都是从左向右扫描文本对比实现的,但是BoyerMoore是从右向组走扫描实现的,具体实现:

static const NSInteger LetterCount=256;

@interface BoyerMoore()

@property (strong,nonatomic) NSString *pattern;

@property (strong,nonatomic) NSMutableArray *rightArr;

@end

@implementation BoyerMoore

-(instancetype)initWithPattern:(NSString *)pattern{
   self=[super init];
   if (self) {
       self.pattern=pattern;
       [self setup];
   }
   return self;
}

-(NSInteger)search:(NSString *)text{
   NSInteger tlen=text.length;
   NSInteger plen=self.pattern.length;
   NSInteger skip;
   for (NSInteger i=0; i<=tlen-plen; i+=skip) {
       skip=0;
       for (NSInteger j=plen-1;j>=0;j=j-1) {
           if ([self.pattern characterAtIndex:j]!=[text characterAtIndex:(i+j)]) {
               NSInteger index=[text characterAtIndex:(i+j)];
               skip=j-[self.rightArr[index] integerValue];
               break;
           }
       }
       if (skip==0) {
           return i;//匹配成功
       }
   }
   return -1;//匹配失败
}

-(void)setup{
   self.rightArr=[[NSMutableArray alloc]init];
   for (NSInteger i=0; i<LetterCount; i++) {
       [self.rightArr addObject:@(-1)];
   }
   
   for (NSInteger j=0; j<self.pattern.length; j++) {
       NSInteger index=[self.pattern characterAtIndex:j];
       self.rightArr[index]=@(j);
   }
}

@end

Rabin-Karp指纹字符串查找

Rabin和Karp这种算法和前三种走了完全的不同的方向,基于散列的查找,也就是每一个字符串都有一个对应的Hash值,如果Hash值相同的话,那么匹配成功,基本效率保持在O(n).

static  const NSInteger Q=997;
static  const NSInteger LetterCount=10;
@interface RabinKarp()

@property (strong,nonatomic) NSString *pattern;

@property (assign,nonatomic) NSInteger pCount;
@property (assign,nonatomic) NSInteger RM;
@property (assign,nonatomic) NSInteger pathHash;

@end

@implementation RabinKarp

-(instancetype)initWithPattern:(NSString *)pattern{
   self=[super init];
   if (self) {
       self.pattern=pattern;
       self.pCount=self.pattern.length;
       [self setup];
   }
   return self;
}

-(NSInteger)search:(NSString *)text{
   NSInteger tlen=text.length;
   NSInteger txtHash=[self hash:text length:self.pCount];
   //开头匹配成功
   if (self.pathHash==txtHash) {
       return 0;
   }
   
   for (NSInteger j=self.pCount; j<tlen; j++) {
       //逐一匹配
       NSInteger chValue=[[text substringWithRange:NSMakeRange(j-self.pCount, 1)] integerValue];
       txtHash=(txtHash+Q-self.RM*chValue%Q)%Q;
       NSInteger num=[[text substringWithRange:NSMakeRange(j, 1)] integerValue];
       txtHash=(txtHash*LetterCount+num)%Q;
       if (self.pathHash==txtHash) {
           return j-self.pCount+1;
       }
   }
   return -1;
}

-(void)setup{
   self.RM=1;
   for (NSInteger i=1; i<=self.pCount-1; i++) {
       self.RM=(LetterCount*self.RM)%Q;
   }
   self.pathHash=[self hash:self.pattern length:self.pCount];
}

-(NSInteger)hash:(NSString *)key length:(NSInteger)len{
   NSInteger hash=0;
   for (NSInteger j=0; j<len; j++) {
       NSInteger chValue=[[key substringWithRange:NSMakeRange(j, 1)] integerValue];
       hash=(LetterCount*hash+chValue)%Q;
   }
   return hash;
}

-(NSInteger)hashStr:(NSString *)key length:(NSInteger)len{
   NSInteger hash=0;
   for (NSInteger j=0; j<len; j++) {
       NSInteger chValue=[key characterAtIndex:j];
       hash=(LetterCount*hash+chValue)%Q;
   }
   return hash;
}

@end

测试代码:

   //1.暴力查找
   NSString *text=@"She is a girl";
   NSString *pattern=@"girl";
   if ([SearchMatch simpleMatchPattern:text pattern:pattern]) {
       NSLog(@"%@ is contains %@",text,pattern);
   }else{
       NSLog(@"%@ is not contains %@",text,pattern);
   }
   //2.简单回退
   text=@"FlyElephant";
   pattern=@"Elem";
   NSInteger matchIndex=[SearchMatch simpleBackMatchPattern:text pattern:pattern];
   if (matchIndex>0) {
       NSLog(@"%@ contains %@ ,index %ld",text,pattern,matchIndex);
   }else{
       NSLog(@"%@ is not contains %@",text,pattern);
   }
   //3.KMP查找
   NSString *kmpText=@"BCBAABACAABABACAA";
   NSString *kmpPattern=@"ABABAC";
   kmpText=@"AAACCBBDEF";
   kmpPattern=@"A";
   KMPSearch *kmp=[[KMPSearch alloc]initWithPattern:kmpPattern];
   NSInteger result=[kmp search:kmpText];
   if (result>=0) {
       NSLog(@"%@ contains %@ ,Match Begin index:%ld",kmpText,kmpPattern,result);
   }else{
       NSLog(@"%@ not contains %@ ",kmpText,kmpPattern);
   }
   //4.BoyerMoore查找
   NSString *bmText=@"FlyElephant";
   NSString *bmPattern=@"Fly";
   BoyerMoore *bm=[[BoyerMoore alloc]initWithPattern:bmPattern];
   NSInteger bmResult=[bm search:bmText];
   if (bmResult>=0) {
       NSLog(@"%@ contains %@,Begin Index:%ld",bmText,bmPattern,bmResult);
   }else{
       NSLog(@"%@ not contains %@",bmText,bmPattern);
   }
   
   //5.RabinKarp 查找
   NSString *rmText=@"3141592653589793";
   NSString *rmPattern=@"26535";
   RabinKarp *rm=[[RabinKarp alloc]initWithPattern:rmPattern];
   NSInteger rmResult=[rm search:rmText];
   if (rmResult>=0) {
       NSLog(@"%@ contains %@,Begin Index:%ld",rmText,rmPattern,rmResult);
   }else{
       NSLog(@"%@ not contains %@",rmText,rmPattern);
   }
FlyElephant.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容