【JavaOJ 题集】字符串匹配问题-BF算法 and KMP算法

简介: JavaOJ 题集 & 字符串匹配问题 & BF算法 & KMP算法

JavaOJ 题集 & 字符串匹配问题 & BF算法 & KMP算法

背景(from百度百科):

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。


KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) [1] 。


大抵意思就是,字符串找子串咯!

就是,给一个字符串s1和一个子字符串s2,在s1里找到与s2完全一样的一个片段。

【力扣28原题链接】


1. BF暴力算法

这种方法简单粗暴,如下图


原字符串每个下标都尝试一下,每次错误(图示为了简约写为 j ,代码为了明确写为 subI)subI都置为0,从头测试,

如果最终subI都没有达到子字符串的长度,那么就返回-1【即不存在】,

否则返回 i - subI

【即该子字符串在原字符串的头下标】

【因为i与subI走的步数一致】

public static int BFMatch(String str, String subStr) {
    //预防一些奇葩事例
    if(str == null || subStr == null ||
            str.length() == 0 || subStr.length() == 0) {
        return -1;
    }
    int i = 0;
    int subI = 0;
    while(i < str.length() && subI < subStr.length()) { 
        //相同一起走一步
            if(str.charAt(i) == subStr.charAt(subI)) {
                i++;
                subI++;
            }else {
                //i要回退
                i = i - subI + 1;
                //subI要回退为初始位置0
                subI = 0;
            }
    }
    return subI == subStr.length() ? i - subI : -1;
}


1.1 测试

public static void main(String[] args) {
    System.out.println(BFMatch("mississippi", "issip"));
}



2. KMP算法

在之前的计算之中,我们的subI都直接清0,但是实际上,应该是有一部分还可以保留

如下图:


没错,subI并没有直接清为0,而是找到了前面可以用的一部分,继续走,而【i】,从始至终都从未回退!


要实现这个算法,很重要的一个点就是,subI回退到哪?


我们需要一个next数组来记录subStr的每一个停下的话要回退到哪。

接下来我们来研究一下其中的规律吧!

2.1 基础模板

阻隔一些特殊用例

两个下标

两个字符串长度,避免反复使用 length() —>提高效率

转化为两个字符串数组,避免反复使用charAt() —> 提高效率

得到next数组

getNext(next, subStrArr, subLen);

匹配一下

subI = next[subI]; ===》subI的回退
public static int strStr(String str, String subStr) {
    //1.***********************
    if(str == null || subStr == null ||
            str.length() == 0 || subStr.length() == 0) {
        return -1;
    }
    //2.***********************
    int i = 0;
    int subI = 0;
  //3.***********************
    int strLen = str.length();
    int subLen = subStr.length();
    //4.***********************
    char[] strArr = str.toCharArray();
    char[] subStrArr = subStr.toCharArray();
    //5.***********************
    int[] next = new int[subLen];
    next[0] = -1;
    if(subLen != 1) {
        getNext(next, subStrArr, subLen);
    }
    //6.***********************
    while(i < strLen && subI < subLen) {
        if(subI == -1 || strArr[i] == subStrArr[subI]) {
            i++;
            subI++;
        }else {
            subI = next[subI];
        }
    }
    return subI == subLen ? i - subI : -1;
}

2.1.1 获得next数组

理论基础:


设数字k为其跳回的位置, i为停下来的位置

那么字符串 【0 ,k - 1】对应的就是subStr停下来前的一小段【i - k, i - 1】(重点)

注意:subStr停下的那个位置,之前有k个在原字符串对应位置走过!

注意:两小段可以相交但是不能相等!,否则跟没回退一样,等着死循环吧

即,这一段与原字符串对应位置一致

如下所示

那么 k 对应的值就是前面重复部分的长度【三种值, -1 , 0 , > 0】【-1, 就是首元素的回退,当然使用的时候要判断一下,避免数组越界访问】


接下来我们要来看看如何求具体某一个下标对应的next值


假设,我说假设!


如果我知道一个 next[X - 1],next[X]可以求吗?

如果可以的话,是不是就可以通过 递推:next[0] --> next[1] -->next[2] —> … —> next[X]


如上图,要求next[X],知道next[X - 1],怎么求 next[X] 呢?


Ⅰ.如果 k == next[x - 1],满足 subStr.charAt( k ) == subStr.charAt(X - 1)


有如下关系(伪代码)


【0, k -1】 + 【k - 1, k】 == 【i - k, i - 1】 + 【i - 1, i】;


也就是


【0, k】 == 【i - k, i】;


所以next[X] = k + 1


Ⅱ. 如果不相等,则说明 “不能-少-回退那么多”,要 “多回退一点”


我们只需要让next[k]再回退几次,直到与满足上面情况,或者无法再回退,【-1的设计就是为了如此】


如下图(特别重要!)



理论基础就是:本质上递推求next[X],的原理就是,


【红串】 == 【橙串】,相当于两段字符串以及完美匹配了,


最后,我们再加上一个元素,如果不匹配!== 》


则【红串】(类比,subStr)的尾倒退一次,

【橙串】也减低了要求,不需要匹配那么远了,也没办法匹配那么远

这张图的结束就是===》next[X] = 0


2.1.2 代码实现

int[] next = new int[subLen];
next[0] = -1;
if(subLen != 1) { //只有一个字符就不需要这个方法了
    getNext(next, subStrArr, subLen);
}
public static void getNext(int[] next, char[] arr, int len) {
    //白手起家
    int k = 0;
    //白手起家所需要的原材料,把值能确定的先赋值上
    //这种【递推思想】的题,有原材料做起来会更好,好比珍珠成型之前也需要一颗小石子
    next[1] = 0;
    int i = 2;
    //遍历每一位
    while(i < len) {
        //相等或者无法再次回退
        if(k == -1 || arr[i - 1] == arr[k]) {
            next[i] = k + 1;
            i++;
            k++;//k变长
        }else {
            k = next[k];//k变短
        }
    }
}


2.1.3 测试


成功

如果不转化为字符数组,是没办法达到 0ms 的;

3. KMP算法优化版


还记不记得这张图?


没错,我们发现了一些赘余部分,

就是19跳到15,还是d,还是得回退,接下来可以直到15跳到11,还是d!,

这样下去跳到 3,接下来就是 0,既然如此为什么不直接到 0呢?

接下来我们设计一个方法getNextValue(),去修改一些next值,避免上面的状况


public static int[] getNextValue(int[] next, int len, char[] arr) {
        int[] nextValue = new int[len];
        nextValue[0] = -1;
        //遍历整个数组
        for (int i = 1; i < len; i++) {
            int n = next[i];
            if(arr[i] == arr[n]) {
                nextValue[i] = nextValue[n];
            }else {
                nextValue[i] = n;
            }
        }
        return nextValue;
    }


3.1 如果回退后的字符跟回退前一致

if(arr[i] == arr[n]) {
                nextValue[i] = nextValue[n];
            }


那么继承回退后的那个nextValue对应值

3.2 如果回退后的字符跟回退前不一致

else {
                nextValue[i] = n;
            }

保留自己的next对应值

3.3 测试

//在strStr()方法,匹配前,补充上如下代码,并之后使用next的都改成使用nextValue;

int[] nextValue = getNextValue(next, subLen, subStrArr);



文章到此结束!谢谢观看

可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!


这是我的代码仓库!(在马拉圈的23.2里)代码仓库

字符串匹配算法 · 具体位置

邮箱:2040484356@qq.com


s:103
+关注
目录
打赏
0
0
0
0
34
分享
相关文章
|
5月前
|
两个字符串匹配出最长公共子序列算法
本文介绍了最长公共子序列(LCS)问题的算法实现,通过动态规划方法求解两个字符串的最长公共子序列,并提供了具体的编程实现细节和示例。
143 1
两个字符串匹配出最长公共子序列算法
|
5月前
|
第四章 KMP算法理论基础
第四章 KMP算法理论基础
49 0
|
5月前
|
KMP算法
KMP算法
59 0
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
7月前
|
KMP算法
KMP算法
53 0
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
本研究基于MATLAB 2022a,使用GRU网络对QAM调制信号进行检测。QAM是一种高效调制技术,广泛应用于现代通信系统。传统方法在复杂环境下性能下降,而GRU通过门控机制有效提取时间序列特征,实现16QAM、32QAM、64QAM、128QAM的准确检测。仿真结果显示,GRU在低SNR下表现优异,且训练速度快,参数少。核心程序包括模型预测、误检率和漏检率计算,并绘制准确率图。
83 65
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
基于PSO粒子群优化的CNN-LSTM-SAM网络时间序列回归预测算法matlab仿真
本项目展示了基于PSO优化的CNN-LSTM-SAM网络时间序列预测算法。使用Matlab2022a开发,完整代码含中文注释及操作视频。算法结合卷积层提取局部特征、LSTM处理长期依赖、自注意力机制捕捉全局特征,通过粒子群优化提升预测精度。适用于金融市场、气象预报等领域,提供高效准确的预测结果。
基于Big-Bang-Big-Crunch(BBBC)算法的目标函数最小值计算matlab仿真
该程序基于Big-Bang-Big-Crunch (BBBC)算法,在MATLAB2022A中实现目标函数最小值的计算与仿真。通过模拟宇宙大爆炸和大收缩过程,算法在解空间中搜索最优解。程序初始化随机解集,经过扩张和收缩阶段逐步逼近全局最优解,并记录每次迭代的最佳适应度。最终输出最佳解及其对应的目标函数最小值,并绘制收敛曲线展示优化过程。 核心代码实现了主循环、粒子位置更新、适应度评估及最优解更新等功能。程序运行后无水印,提供清晰的结果展示。
基于CS模型和CV模型的多目标协同滤波跟踪算法matlab仿真
本项目基于CS模型和CV模型的多目标协同滤波跟踪算法,旨在提高复杂场景下多个移动目标的跟踪精度和鲁棒性。通过融合目标间的关系和数据关联性,优化跟踪结果。程序在MATLAB2022A上运行,展示了真实轨迹与滤波轨迹的对比、位置及速度误差均值和均方误差等关键指标。核心代码包括对目标轨迹、速度及误差的详细绘图分析,验证了算法的有效性。该算法结合CS模型的初步聚类和CV模型的投票机制,增强了目标状态估计的准确性,尤其适用于遮挡、重叠和快速运动等复杂场景。
基于Adaboost的数据分类算法matlab仿真
本程序基于Adaboost算法进行数据分类的Matlab仿真,对比线性与非线性分类效果。使用MATLAB2022A版本运行,展示完整无水印结果。AdaBoost通过迭代训练弱分类器并赋予错分样本更高权重,最终组合成强分类器,显著提升预测准确率。随着弱分类器数量增加,训练误差逐渐减小。核心代码实现详细,适合研究和教学使用。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等