KMP算法,BF算法

串、BF算法、KMP算法

概述

需要掌握:
  1、串的相关概念,串与线性表之间的异同

  2、顺序串,和链串中串的基本的基本运算算法设计
  3、模式匹配算法BF、和KMP算法

串的相关概念,串与线性表之间的异同

复制代码
str = "A(0)A(1)...A(n-1)"

1、n 是串的长度

2、n = 0时为"空串"

3、str = "  "这样的由一个或多个的空格组成的为"空白串"

4、一个串当中的任意连续字符组成的子序列为"子串",包含所有字串的串就是"主串"

举例:  
  str = "123456.....n" 它的子串有多少?
  答:空串"1",每一个字符也是一个子串"n",每两个连续字符也是一个子串"n-1"没三个连续的字符也是一个子串"n-2"....str本身也是一个子串
    子串 = 1+n+(n-1)+(n-2).....2+1 = n(n+1)/2+1
注意:
  学习子串有利于学习后面的算法

串的存储结构:
  1、串的顺序存储结构————顺序串
    这个串存储在一组连续的存储单元(数组就是分配了一块存储单元)
  2、串的链式存储结构————链串
    将串存储在多个存储单元里,但是每个存储单元是有联系的(链表)

线性表:
  顺序存储结构
  链式存储结构
注意:
  这个顺序存储结构和线性存储结构是计算机对他们的内存分配而言,对我们的使用而言,串就是一个完整的串
复制代码

模式匹配算法BF、和KMP算法

复制代码
问题描述:
  已有两个串s、t,在串s里找与t对应的子串,通常把s称为目标串,t为模式串。在目标串里找到模式串,存在该模式串返回true,不存在返回false

解决办法:
  1、BF算法(暴力算法)
  2、KMP算法

特别提醒:
  我们只是利用者,这些算法都是牛人想到的,我们只是理解加记忆,不用纠结与为什么我们想不到,我也焦虑与他们为啥这么牛
  接下来讲解我对他们的理解与实现
复制代码

BF算法

复制代码
解题思路:
  1、取出模式串的第一个字符,与目标串的第一个进行比对
    a.比对成功
      取出模式串的下一个字符,与目标串的下一个比对(然后又是a,b)
    b.比对不成功
      将目标串回溯到第二个字符,模式串回溯到第一个字符
    也就说目标串从索引为"0"开始,看后面的字符串是否与模式串匹配,不匹配,目标串就从索引为"1"开始,看后面的是否匹配...然后3.4.5....直到目标串遍历完
  2、结束条件
    c.当把目标串走完,也没有找到与模式串匹配的,返回false(也就是目标串遍历完)
    d.当找到目标串返回true(也就是模式串全部走完)
注意:
  如果没有理解,建议去找下图片呈现详细过程
复制代码
复制代码
public class ViolenceMatch {

    public static boolean violenceMatch(String str1, String str2) {
        char[] s = str1.toCharArray();// 目标串
        char[] t = str2.toCharArray();// 模式串
        
        int index  = 0;//记录模式串匹配到的位置 
        for(int i = 0;i < s.length;i++) {
            //如果匹配成功
            if(s[i] == t[index]) {
                index++;
                //当把最后一个字符串也匹配成功了
                if(index == t.length) {
                    return true;
                }
                else
                    continue;
            }
            //如果没成功,就回溯
            if(s[i] != t[index]) {
                index = 0;
                i = i-index; //难点在这儿
                continue;
            }
        }
        return false;
    }
//分析i = i - index 的由来,(这就是上面的 b )
    a  a  a  a  b  目标串  a  a  b  模式串
    第一趟:
    a  a  a  a  b (i = 2)
    a  a  b      (index = 2)
    这时候目标串需要从索引为 1 开始(i - index)+1 = 2-2+1

    第二趟:
    a  a  a  a  b (i = 3)
       a   a  b    (index = 2)
    这时候目标串需要从索引为 2 开始,(i - index)+1 = 3-2+1
    
    第三趟:
    a  a  a  a  b
          a  a  b
本来我想写一个flag记录目标串需要从哪里开始匹配,但是写半天没写出来(可能愚笨把,哭泣!)
复制代码

 kmp算法

复制代码
解题思路:
  1、假设计算机在依次比对目标串和模式串的时候,能够记住已经匹配的字符是什么
  2、目标串t = "abbcabab",模式串s = "af" ,把他们转换成字符数组
    我们比较t[0] == s[0], t[1] != t[1]这时候不相等,如果按照BF算法将比较t[1] 与 s[0]
    但是我们假设计算机记住了已经比较的字符,那我们知道t[1] != a;那我们就直接这样比较了t[2] == s[0],这样是不是就不用回溯那么多了;
  3、目标串t = "abcabcd",模式串s = "abcabf" ,
把他们转换成字符数组
    我们比较t[0] == s[0],t[1] == s[1],t[2] == s[2],t[3] == s[3],t[4] == s[4],t[5] != t[5]
    如果是暴力解法的话,将比较t[1] != s[0],t[2] != s[0],t[3] == s[0],....他要比较很多次才比较到这里来
   但是我么假设计算机记住了我们已经比较的字符,那我们知道t[0]t[1] == t[3]t[4] == ab == s[0]s[1] == s[3]s[4]
    那我们就直接比较s[2]后面的字符和t[4]后面的字符
   建议上述过程画图比较(就是说BF一个一个的移动,KMP是跳着移动)
  4、那我们如何使计算机像人一样能够记住和识别呢??"公共前缀表"推荐好文 https://www.cnblogs.com/zzuuoo666/p/9028287.html
  5、这里很抱歉,我感觉我讲的不是明白,但是应该还是有助于大家理解的
复制代码
复制代码
 1     public int[] getPrifix(String str) {
 2         //转换成字符数组方便操作
 3         char[] charArray = str.toCharArray();
 4         
 5         //定义一个数组接收str字符串的公共前后缀长度
 6         int[] prifix = new int[charArray.length];
 7         
 8         //prifixLength  接收字符串公共前后缀长度
 9         int prifixLength = 0;
10         
11         //遍历charArray
12         for(int q = 0;q < charArray.length;q++) {
13             //p 记录后缀的头指针,起始位置
14             int p = 1;
15             //i 前缀的头指针,j 后缀的头指针
16             for(int i = 0, j = 1;j <= q;) {
17                 if(charArray[i] == charArray[j]) {
18                     i++;
19                     j++;
20                     prifixLength++;
21                     continue;
22                 }
23                 if(charArray[i] != charArray[j]) {
24                     i = 0;
25                     j = p+1;//回溯后缀指针到记录的下一个位置
26                     p = j;//记录后缀的头指针,起始位置
27                     prifixLength = 0;
28                     continue;
29                 }
30             }
31             prifix[q] = prifixLength;
32             prifixLength = 0;
33         }
34         return prifix;
35     }
复制代码

 

复制代码
/**
     * @param str1 目标串
     * @param str2 模式串
     * @return true: 目标串存在与模式串相等的子串,false 目标串不存在与模式串相等的子串
     */
    public static boolean KMP(String str1, String str2) {
        // 转换模式串,目标串为字符数组,方便操作
        char[] s = str1.toCharArray();
        char[] t = str2.toCharArray();

        int[] prifix = kmp.getPrifix(str2);

        // 定义两个指针
        int s_index = 0;
        int t_index = 0;

        while (true) {
            if (s_index >= s.length || t_index >= t.length) {
                return t_index >= t.length ? true : false;
            }
            if (s[s_index] == t[t_index]) {
                s_index++;
                t_index++;
                continue;
            }
            if (s[s_index] != t[t_index] && t_index != 0) {
                t_index = prifix[t_index - 1];
                continue;
            }
            if (s[s_index] != t[t_index] && t_index == 0) {
                // 为什么是它++呢,因为t_index == 0,这时候s[s_index+1] == t[t_index],也就是说目标串后移一位
                s_index++;
                continue;
            }
        }
    }
复制代码
posted @   月亮也失约  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示