【数据结构与算法】字符串匹配 - 暴力算法

【数据结构与算法】字符串匹配 - 暴力算法

通过CTRL + F查找文本这种操作相信每个人都有,在一段文本中查找另外一段文本,这个操作就叫做字符串的模式匹配,比如在AABABCDEF中查找ABCD,那么AABABCDEF被叫做文本串(也叫主串),ABCD被叫做为模式串,通常主串的长度使用N来表示,模式串的长度使用M来表示,上面两个串的匹配情况如下图所示:
匹配
正如上图绿色部分那样,文本串中包含模式串,结果就是匹配到了。

字符串模式匹配的算法有很多,比如暴力算法、KMP算法、BM算法、BK算法等,一一来介绍。

BF算法

BF算法也叫做暴力算法,思路简单,很容易理解,首先将文本串和模式串左对齐,如果出现不匹配的情况,则模式串整体向后移动一位,否则就匹配了,依然拿下图来说明:
匹配

两个串左对齐开始匹配,如下图:

红色处字符不相等,则停止比较后面的字符,将整个模式串向后移动,再次匹配:
不匹配

红色处字符也不相等,则继续向后移动模式串,重新匹配:
在这里插入图片描述
红色处字符也不相等,则继续向后移动模式串,重新匹配:
在这里插入图片描述

此时找到了匹配结果。

通过上面的图解分析,其实能够很好地总结出匹配的步骤:

  1. 循环文本串,文本串中的当前字符(当前指针指向的字符)开始向后以此与文本串比较。
  2. 比较的时候,如果遇到字符不相等的情况,那么就需要停止比较,文本串指针需要向后移动一位,继续与模式串比较(从模式串头开始以此比较)
  3. 如果与模式串的所有字符以此比较都相等,那么说明匹配到了。

按照上面的分析,我可以定义一个指针i指向文本串,定义一个指针j指向模式串:

第一次匹配:

在这里插入图片描述
本次匹配,以为i = 0, j = 0开始匹配,当j = 1的时候字符不匹配(text[i + j] != pattern[j]),也就是text[1] != pattern[1],接下来指针i要向后移一位,而指针j则需要设置为0,继续匹配。

第二次匹配:
在这里插入图片描述
匹配以i = 1开始,当j = 2的时候字符不匹配text[i + j] != pattern[j],也就是text[3] != pattern[2],此时指针i需要继续向后移动一位,指针j还是回到头部

第三次匹配:
在这里插入图片描述
匹配以i = 2开始,当j = 0的时候字符不匹配text[i + j] != pattern[j],也就是text[2] != pattern[0],此时指针i需要继续向后移动一位,指针j还是回到头部

第三次匹配:
在这里插入图片描述
匹配以i = 3开始,找到了模式串,但是j还是会向后移动的,此时j等于模式串的长度m

代码如下:

class Solution {
    public int strStr(String text, String pattern) {
        // 文本串长度为n
        int n = text.length();
        // 模式串长度为m
        int m = pattern.length();
        for (int i = 0; i < n - m; i++) { // ①
            int j = 0;
            while (j < m) {
                if (text.charAt(i + j) != pattern.charAt(j)) { // ②
                    break;
                }
                j++; // ③
            }
            if (j == m) { // ④
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        System.out.println(new Solution().strStr("AABABCDEF", "ABCD"));
    }
}

①:此处条件是 i <= n - m,这是因为如果主串剩余的长度如果没有模式串长,后面的字符也就不用进行比较了。
在这里插入图片描述
如上图,绿色位置到最后长度是3,而模式串的长度是4,明显没必要比较,一定不会匹配。
②:当遇到字符不匹配的时候,后续字符是无需比较的,直接结束本次循环。
③:如果字符相等,则继续比较后续字符。
④:j == m说明整个模式串都匹配,这里我返回了匹配的开始索引位置。


上面的算法实现中,文本串i每次匹配都保持不变(因为是通过text.charAt(i + j) != pattern.charAt(j)进行比较的),只有一次匹配完成后,才会向后移动。

暴力算法还有另外一种实现方式,每次匹配下,每个字符进行比较的时候,指针i也会随着一起向后移动,当发生不匹配的时候指针i需要回溯,原理如下图所示:
在这里插入图片描述

道理上与之前的没啥区别,只是指针计算上有些差异而已,代码如下:

class Solution {
    public int indexOf(String text, String pattern) {
        // 煮串长度为n
        int n = text.length();
        // 模式串长度为m
        int m = pattern.length();
        // 煮串指针
        int i = 0;
        // 模式串指针
        int j = 0;
        while(i < n && j < m) {
            // 获取主串的字符ci和模式串的字符cj
            char ci = text.charAt(i);
            char cj = pattern.charAt(j);
            // 如果字符相等,则匹配后面的字符
            if (ci == cj) { // ①
                i++;
                j++;
            } else {
                // 如果字符不相等,那么就需要回溯
                i = i - j + 1; // ② 
                j = 0; // ③
            }
            if (j == m) { // ④
                return i - m;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        System.out.println(new Solution().indexOf("AABABCDEF", "ABCD"));
    }
}

①:对应位置字符相等,就继续比较后面的字符。
②:字符不相等,模式串指针回到头部,文本串的指针i应该回到上次比较的开始的位置的下一位置,为什么是i = i - j + 1;呢,看如下图:
在这里插入图片描述
i = 3, j = 2的时候失配了,考虑下i从索引为1的位置做了多少步到了3的位置,就是走了j步,那么i - j后,那么i就回到了本次比较的开始位置,下一次比较要从其后面开始所以再加上1
③:j == m说明正好都匹配,(这里我开始想错了,我在想应该是 (j - 1) == m,但实际上最后j还是会再加1,然后才不满足循环条件,不要犯错哦😄)。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值