美文网首页
KMP(三) 字符串快速匹配示例

KMP(三) 字符串快速匹配示例

作者: hehtao | 来源:发表于2017-09-05 16:32 被阅读150次

概述:本文主要讲解KMP实现字符串快速查找的一个Demo;不了解KMP的同学可以参考:
KMP(一) 模式匹配算法推导 --《部分匹配表》
KMP(二) 模式匹配算法实现
KMP(三) 字符串快速匹配示例
字符串快速匹配Demo下载


一. 目的效果:

QQ20170905-155723-HD.gif
对,就是这样一个类似于网页检索的效果;实现起来也比较简单;
Talk is cheap ,Show me your code!

二. 单纯KMP的缺陷:

先来温习下KMP(二) 模式匹配算法实现当中的实现方案:

#pragma mark - getNext
-(NSArray<NSNumber *> *)getNextWithString:(NSString *)string{
    NSMutableArray<NSNumber *> * next = [NSMutableArray arrayWithCapacity:string.length];
    next[0] = @(-1);//初始化
    int j= 0,k= -1; //j:记录当前下标; k记录当前位的next

    while (j <  string.length - 1) {
        if (k== -1 || [string characterAtIndex:j] == [string characterAtIndex:k]) { // 比较当前(j)字符与当期位next处字符是否相等
            next[++j] = @(++k); // 移动下标,并求解下一位的next;
        }else{
            k = [next[k] intValue]; // 回溯当前位的next
        }
    }
    return next;
}
-(NSArray<NSString *> *)indexKMPwithParentString:(NSString *)pString andSubstring:(NSString *)sString{
    NSArray *next = [self getNextWithString:sString]; // 计算next数组
    int index_s = 0 ,index_p = 0;    //标记 pString 和 sString 的当前比对位置
    NSMutableArray<NSString *> *ranges = [NSMutableArray array]; //用于保存匹配结果的range位置

    while ((index_p < pString.length) && (index_s < sString.length)) { // 一直比对至两个字符串结尾
        if ([pString characterAtIndex:index_p] == [sString characterAtIndex:index_s]) { // 当前比对位置的字符相等,则移动P和S继续下一位比对
            if ((index_s == sString.length - 1) && (index_p != pString.length -1)) { // 字串S比对至末尾,但是主串P未到末尾,即是说字串匹配成功,但是尚需确定主串的后续位置是否能匹配,故继续比对
                index_s = 0; //将字串S当前比对位置移至起始位置
                [ranges addObject:NSStringFromRange(NSMakeRange(index_p - sString.length + 1 , sString.length))]; //保存本次匹配的位置结果
                continue; // 完成一次匹配,跳出本次循环 index_p 不再移动
            }
            index_p ++; // 向后移动P的当前位置
            index_s ++; // 向后移动S的当前位置
        }else{  //当前比对位置的字符不相等,则保持主串P位置不回溯,根据next数组回溯字串S
            if (index_s != 0) { //特别处理 next[0] = "-1"的情况
                index_s = [next[index_s] intValue]; // 根据next回溯当前字串S的比对位置
            }else{
                 index_p ++; //如果起始位置不匹配,则直接移动主串P
            }
        }
    }
    return ranges;
}

当目标子串仅有一个字符时候,显然-(NSArray<NSString *> *)indexKMPwithParentString: andSubstring:方法存在缺陷,效率低于朴素查找方法;So,我们要做下改进,当字串仅有一个字符时,采用朴素算法,当然,也顺便对上节中的代码稍加整理规范;

三.字符串快速匹配完整实现:

  • 首先制作UI界面,一个Label一个TextField 就略过了,给TextField添加一个监听:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldTextDidChangeNotification) name:UITextFieldTextDidChangeNotification object:nil];
  • 实现TextFieldTextDidChangeNotification方法:
#pragma mark - UITextFieldTextDidChangeNotification,AttributedString相关
-(void)textFieldTextDidChangeNotification{
    //    重置字体背景色
    [_attString setAttributes:@{NSBackgroundColorAttributeName:[UIColor whiteColor]} range:NSMakeRange(0, _label.text.length)];
    _label.attributedText = _attString;
    _ranges = [self indexKMPwithParentString:_label.text andSubstring:_tf.text];
    //    标记匹配的字体背景色
    if (_ranges) {
        for (NSString *string  in _ranges) {
            [_attString setAttributes:@{NSBackgroundColorAttributeName:[UIColor yellowColor]} range:NSRangeFromString(string)];
            _label.attributedText = _attString;
        }
    }
}

#pragma mark - indexKMP
-(NSArray<NSString *> * _Nullable)indexKMPwithParentString:(NSString *)pString andSubstring:(NSString *)sString{
    NSArray<NSString *> *ranges = [NSArray array];
    if (sString.length > 1) { //检索目标字符串为多个字符
        ranges =  [[self indexKMPwithParentString:pString andMultipleChar:sString] copy];
    }else{ //检索目标字符串为单个字符
        ranges = [[self indexKMPwithParentString:pString andSingleChar:sString] copy];
    }
    return ranges;
}

  • next数组的计算:
#pragma mark - getNext
-(NSArray<NSNumber *> *)getNextWithString:(NSString *)string{
    NSMutableArray<NSNumber *> * next = [NSMutableArray arrayWithCapacity:string.length];
    next[0] = @(-1);//初始化
    int j= 0,k= -1; //j:记录当前下标; k记录当前位的next

    while (j <  string.length - 1) {
        if (k== -1 || [string characterAtIndex:j] == [string characterAtIndex:k]) { // 比较当前(j)字符与当期位next处字符是否相等
            next[++j] = @(++k); // 移动下标,并求解下一位的next;
        }else{
            k = [next[k] intValue]; // 回溯当前位的next
        }
    }
    return next;
}

  • 检索匹配的实现:
  1. 用于字串不是单个字符的情况,KMP
#pragma mark - 用于字串不是单个字符的情况
-(NSArray<NSString *> * _Nullable)indexKMPwithParentString:(NSString *)pString andMultipleChar:(NSString *)sString{
    
    NSUInteger sLength = sString.length;
    NSUInteger pLength = pString.length;
    if (sLength < 2 || pLength == 0) return nil;
    NSArray *next = [self getNextWithString:sString]; // 计算next数组
    int index_s = 0 ,index_p = 0;    //标记 pString 和 sString 的当前比对位置
    
    NSMutableArray<NSString *> *ranges = [NSMutableArray array]; //用于保存匹配结果的range位置
    while ((index_p < pLength) && (index_s < sLength)) { // 一直比对至两个字符串结尾
        if ([pString characterAtIndex:index_p] == [sString characterAtIndex:index_s]) { // 当前比对位置的字符相等,则移动P和S继续下一位比对
            if ((index_s == sLength - 1) && (index_p != pLength -1)) { // 字串S比对至末尾,但是主串P未到末尾,即是说字串匹配成功,但是尚需确定主串的后续位置是否能匹配,故继续比对
                index_s = 0; //将字串S当前比对位置移至起始位置
                [ranges addObject:NSStringFromRange(NSMakeRange(index_p - sString.length + 1 , sString.length))]; //保存本次匹配的位置结果
                continue; // 完成一次匹配,跳出本次循环 index_p 不再移动
            }
            index_p ++; // 向后移动P的当前位置
            index_s ++; // 向后移动S的当前位置
        }else{  //当前比对位置的字符不相等,则保持主串P位置不回溯,根据next数组回溯字串S
            if (index_s != 0) { //特别处理 next[0] = "-1"的情况
                index_s = [next[index_s] intValue];
            }else{
                index_p ++;
            }
        }
    }
    return ranges.count ? ranges:nil;    
}

2.用于字串是单个字符的情况,采用朴素匹配效率更高

-(NSArray<NSString *> * _Nullable )indexKMPwithParentString:(NSString *)pString andSingleChar:(NSString *)singleChar{
    if (singleChar.length == 0 || pString.length == 0) return nil;
    int i = 0;
    NSMutableArray<NSString *>* ranges = [NSMutableArray array];
    unichar singchar = [singleChar characterAtIndex:0];
    while (i < pString.length) {
        if (singchar == [pString characterAtIndex:i]) {
            [ranges addObject: NSStringFromRange(NSMakeRange(i, 1))];
        }
        i ++;
    }
    return ranges.count ? ranges:nil;
}

代码中用了大量的循环,应当注意两点:
1.不必要的计算不要引入循环体内;
2.实际应用中可考虑开辟子线程;

相关文章

  • KMP(三) 字符串快速匹配示例

    概述:本文主要讲解KMP实现字符串快速查找的一个Demo;不了解KMP的同学可以参考:KMP(一) 模式匹配算法推...

  • KMP字符串匹配

    KMP字符串匹配

  • KMP算法文章合集

    字符串的查找:朴素查找算法和KMP算法 暴力匹配算法与KMP算法(串的匹配) 字符串查找算法BF和KMP 字符串匹...

  • 白话 KMP 算法

    KMP 算法是计算机字符串匹配的常规算法。wiki 本篇文章借助简单示例,用通俗易懂的方式描述对 KMP 算法的...

  • 数据结构—KMP算法

    KMP算法是一种改进的字符串算法 KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速...

  • 09--KMP

    [toc] KMP算法原理 KMP思想 假设字符串abcdefgab和模式串abcdex,进行匹配,当匹配到x位置...

  • KMP

    KMP有什么用 KMP主要应用在字符串匹配上。 KMP的主要思想是「当出现字符串不匹配时,可以知道一部分之前已经匹...

  • 常见算法题之字符串

    1、KMP算法 参考:july大神的KMP博客细节不摆,该算法由暴力字符串来匹配,具体是由字符串匹配的暴力写法而来...

  • KMP算法

    KMP算法用于子字符串查找(匹配)。 KMP是三个科学家[Knuth-Morris-Pratt]发明的,旨在对暴力...

  • KMP算法的JS实现

    talk is cheap,show me the code: 参考链接: 阮一峰-字符串匹配的KMP算法[KMP...

网友评论

      本文标题:KMP(三) 字符串快速匹配示例

      本文链接:https://www.haomeiwen.com/subject/vpoujxtx.html