寻找子串:BF算法、回溯算法、KMP算法

一、前言

查找子串,是字符串查找的经典题目,大学就有!
比如:

主串:『ABAEABDACAADABACDDA』中查找
子串:『ABACD』

不考虑性能(时间、空间复杂度),大家可能就直接用暴力来求解了:Brute-Force(BF)、Back-Track(BT);但如果要求有一定的性能,那么 KMP 无疑是最经典的算法了(虽然不一定是效率最高的)

二、BF算法

// BF暴力算法
private static int bruteForce(String s, String p) {
    int i = 0;
    for (; i < s.length() - p.length() + 1; i ++) {
        int j = 0;
        for (; j < p.length(); j ++) {
            if (s.charAt(i + j) == p.charAt(j)) {
                continue;
            }
            break;
        }
        if (j == p.length()) {
            return i;
        }
    }
    return -1;
}

三、BT算法

// BT回溯算法
private static int backTrack(String s, String p) {
    int i = 0, j = 0;
    while (i < s.length() && j < p.length()) {
        if (s.charAt(i) == p.charAt(j)) {
            j ++;
        } else {
            i -= j;
            j = 0;
        }
        i ++;
    }
    return j == p.length() ? i - j : -1;
}

四、KMP算法

我们先来通过主串与子串的查找来找找规律:

image.png



再来看另一个数据多点的例子:
17084030-82e4b71b85a440c5a636d57503931415.png

『C』与『B』不匹配时,滑动子串,因为,有共同的前缀『AB』,所以,只需要从子串『index = 2』开始比较就行,如下图:
17084037-cc3c34200809414e9421c316ceba2cda.png

至此我们可以大概看出一点端倪,当匹配失败时,j要移动的下一个位置k。
存在着这样的性质:最前面的k个字符和j之前的最后k个字符是一样的

如果用数学公式来表示是这样的 :

P[0 ~ k-1] == P[j-k ~ j-1]

这个相当重要,如果觉得不好记的话,可以通过下图来理解(即 P[j]发生不匹配时,滑动到k的位置,比较P[k]):


17084056-66930855432b4357bafbf8d6c76c1840.png

主串S 与 子串P 在 j 处发生不匹配时,为什么可以移动到位置 k 推导:

  1. S[i] != P[j]时,必然存在:S[i-j ~ i-1] == P[0 ~ j-1];
  2. P中存在一个k,使得:P[0 ~ k-1] == P[j-k ~ j-1];
  3. 那么:S[i-k ~ i-1] = P[0 ~ k-1]

如果求 k ?我们需要借助一个数组,来记录当每个位置发生不匹配时,需要滑动到的位置,如下图:
Next 就是一个记录的数组(前缀数):

  • 初始标记: next[0] = -1;
  • next[k] = 相同前缀的字符个数;
  • 当 p[j] != p[k] 时, k = next[k] (直观就看下图:『C』和『B』)


    imagexxxx.png
// j为不匹配时的下标,k下标对应的是
// 串[0 ~ k-1] = 串[j-k ~ j-1]
private static int[] getNext(String p) {
    int next[] = new int[p.length()];
    int k = -1, j = 0;
    next[0] = -1;

    while (j < p.length() - 1) {
        if (k == -1 || p.charAt(j) == p.charAt(k)) {
            next[++j] = ++k;
        } else {
            k = next[k]; // next数组中前K个元素,已经有前缀统计
        }
    }
    return next;
}

KMP最终算法如下

// KMP算法
private static int kmp(String s, String p) {
    int next[] = getNext(p);
    int i = 0, j = 0;
    while (i < s.length() && j < p.length()) {
        if (j == -1 || s.charAt(i) == p.charAt(j)) {
            i ++;
            j ++;
        } else {
            j = next[j];
        }
    }
    return j == p.length() ? i - j : -1;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • 字符串匹配KMP算法详解 1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有...
    张晨辉Allen阅读 2,400评论 0 3
  • 一、定义 KMP(Knuth-Morris-Pratt)算法,其实是对暴力查找算法的优化。在暴力查找算法中,用于追...
    null12阅读 824评论 0 0
  • KMP算法是解决字符串匹配的常用算法之一,也就是在主串(比如aabbccdd)中的子串(bc)定位问题。子串称为P...
    激情的狼王阅读 1,020评论 0 1
  • 目标 理解kmp算法思路 如何实现kmp算法 kmp算法 问题描述 给定一个 source 字符串和一个 targ...
    7917398阅读 288评论 0 0
  • 搬运自CSDN博客:KMP算法中特征值数组next的计算与使用 在待匹配字符串P中,对于位置i,我们把P(0~i)...
    hmta_dhs阅读 934评论 0 1