大话数据结构--串的匹配算法

234 阅读2分钟

「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战」。

前言

废话不多,数据结构必须学! 每天更新一章,一篇写不完的话会分成两篇来写~

资料获取

image.png

朴素的模式匹配算法

在文章(相当于一个大串)中找到单词的定位,这种子串的定位操作通常称做串的模式匹配

这是串中很重要的操作之一

实例

我们要找到主串S=“wyjbat”中,找到T = “bat”这个子串的位置。通常要进行下面的步骤。

1.主串S第一位开始,S与T字母进行匹配

image-20211114145400448

2.多次比较从4位开始,S与T,3个字母全匹配,匹配成功

image-20211114145557129


简单的说,就是对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或全部遍历完成为止。

这个算法也太低效了,不玩!!

5.6 KMP模式匹配算法

什么是KMP?

三个科学家D.E.Knuth、 J.H.Morris 和V.R.Pratt,以他们的姓氏命名的

为什么要用KMP算法?

因为暴力匹配效率太低

比如我们要在主串中匹配子串ABCD,这很简单对吧,常规的思路就是一位一位的匹配(这个思路就是暴力匹配算法)

image-20211116093132194

image-20211116093921839

如果对不上了,我们让子串往后移动

image-20211116100717998

上面这种思路可以用代码来实现

 /**
 ​
  * 暴力破解法
 ​
  * @param ts 主串
 ​
  * @param ps 模式串
 ​
  * @return 如果找到,返回在主串中第一个字符出现的下标,否则为-1
 ​
  */
 ​
 public static int bf(String ts, String ps) {
 ​
     char[] t = ts.toCharArray();
 ​
     char[] p = ps.toCharArray();
 ​
     int i = 0; // 主串的位置
 ​
     int j = 0; // 模式串的位置
 ​
     while (i < t.length && j < p.length) {
 ​
        if (t[i] == p[j]) { // 当两个字符相同,就比较下一个
 ​
            i++;
 ​
            j++;
 ​
        } else {
 ​
            i = i - j + 1; // 一旦不匹配,i后退
 ​
            j = 0; // j归0
 ​
        }
 ​
     }
 ​
     if (j == p.length) {
 ​
        return i - j;
 ​
     } else {
 ​
        return -1;
 ​
     }
 ​
 }

上面的程序没问题,只能说不够好!

主串匹配失败的位置除了第一个A之外再也没有A了,我们为什么知道主串前面只有一个A?因为前三个已经匹配过了啊!它们都是匹配的。

我们可以这样保存i不动,移动j

image-20211116100615084

KMP算法的思想就是利用已经部分匹配的有效信息,保持i指针不回溯,通过修改j指针,让子串尽量的移动到有效的位置!

再详细的可以观看这篇文章,写的很好!

我就不更了,日后在更

(原创)详解KMP算法 - 孤~影 - 博客园 (cnblogs.com)

KMP模式匹配算法实现

 /*通过计算返回子串T的next数组。*/
 ?void get_ next ( String T, int *next )
 {
     int i,j;
     i=1;
     j=0;
     next[1]=0;
     while ( i<T[0]) /*此处T[0]表示串T的长度*/
     {
     if (j==0 11 T[i]== T[j]) /* T[i]表示后缀的单个字符,*/
                             /* T[j]表示前缀的单个字符*/
     {
         ++i;
         ++j;
         next[i]=j;
     }
     else
     j= next[j]; /*若字符不相同,则j值回溯*/
     }     
 }
 ​
 /*返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0。*/
 /* T非空,1SposSStrLength (s)。*/
 int Index KMP (String s, String T, int pos )
 {
     int i= pos;/*i用于主串S当前位置下标值,若pos不为1, */
                 /*则从pos位置开始匹配*/
     int j= 1;   /*j用于子串T中当前位置下标值*/
     int next[255] ; /*定义一next数组*/
     get_ next(T, next); /*对串工作分析,得到next数组*/
     while(i<= S[0] &j<= T[0]) /*若i小于S的长度且j小于T的长度时,*/
                             /*循环继续*/
     {
         if (j==01 S[i]== T[j]) /*两字母相等则继续,与朴素算法增加了*/
                                 /*j=0判断*/
         ++i;
         ++j;
         else    /*指针后退重新开始匹配★/
     {
         j = next[j];    /* j退回合适的位置,i值不变*/
     }
     if(j > T[0] )
         return i-T[0] ;
     else
         return 0;
 }

5.7总结

串是由零个或多个字符组成的有限序列,又名字符串。本质上是一种线性表的扩展,相对于线性表关注一个个元素来说,串这种结构更多的是关注它子串的应用问题,如查找、替换等操作。现在的高级语言都有针对串的函数可以调用,我们在使用这些函数的时候,同时也应该要理解它当中的原理,以便于再碰到复杂问题时,可以更灵活的使用