(原创)数据结构之利用KMP算法解决串的模式匹配问题

Posted yewanting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(原创)数据结构之利用KMP算法解决串的模式匹配问题相关的知识,希望对你有一定的参考价值。

 

 

 

给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串S中出现的位置。

输入格式:

输入有两行: 第一行是主串S; 第二行是模式T.

输出格式:

输出相匹配的子串中的第一个字符在主串S中出现的位置。若匹配失败,输出0.

输入样例:

在这里给出一组输入。例如:

aaaaaba
ba

输出样例:

在这里给出相应的输出。例如:

6


解题思路:串的模式匹配有两种:一种是BF算法,一种是KMP算法
基于这道题给的数据,若用BF算法便会超时,所以我们这道题用KMP算法;
那么问题来了,KMP算法到底怎么用的;简单来讲,就是有两个步骤:
1、求模式串的next数组;
2、进行主串与模式串的匹配;
假设主串和模式串分别为
技术图片

 

第一个问题:如何求next数组
??next数组求的是模式串的!!!
下面就以上面给的模式串为例;
next数组便是前缀中的最长相同前后缀,说起来比较绕,什么意思呢,模拟一遍就清楚了;
技术图片

所以对于模式串对应的next数组为
技术图片


这样我们就求出了next数组;
接下来进行模式匹配,其实这样就会有个问题,所以实际上next数组这样是需要改进的;

 

假设我们不改进的话,进行匹配会出现什么问题呢;

进行模式匹配的大概代码如下:

1、即匹配,则i++;j++;

2、不匹配,根据刚刚求出的next数组,进行跳next数组;

下面代码中ssize为主串s的长度,tsize为模式串t的长度;

下面我们就根据代码模拟一遍;

 1     
 2                int i = 0 ;
 3                int j = 0;
 4         while(i<ssize&&j<tsize)
 5         {
 6             
 7                 if(s[i]==t[j])
 8             {
 9                 i++;
10                 j++;
11                 
12             }
13             else 
14             {
15                  j = next1[j];
16             }
17 
18             
19         }
20             
21             
22             if(j==tsize)
23             {
24                 cout <<  i-j+1;
25             }

上面我们求出来的next数组为:

技术图片

现在我们把它们的下面也写上:

 

技术图片

 

现在开始模拟一遍:

 

 技术图片

 

 我们发现匹配到c的时候不匹配了,跳next数组,则跳到下标为0处,变成:

 

 

 技术图片

 

 此时也不匹配,变成应该跳next数组,跳到下标为0处,但是这样就变成死循环了,所以我们应该退一步,将next数组的第0个赋值为-1,且将整个next数组向后移;就不会变成死循环了;

技术图片

再模拟一次:

技术图片

此时不匹配跳next数组;

 

技术图片

 

 

变成:

技术图片

发现a不匹配,跳next数组:

技术图片

 

继续模拟:

技术图片

发现不匹配,所以此时next应该跳到下标为-1处,但是这里没有下标为-1的,所以实际上就是整体向后移;变成:

 

技术图片

 

 

 发现完全匹配了;

那么基于上面的改进,next数组应该怎么写呢:

代码如下:

 1 string s;
 2 string t;
 3 int ssize;
 4 int tsize;
 5 int next1[2000000];
 6         void nextsz(string t,int tsize)
 7         {
 8             next1[0] = -1;     //防止进入死循环,而且到不能匹配时能整体后移
 9             int k = -1;     //是为了调节next数组;
10             int j = 0 ;
11             while(j < tsize-1)
12             {
13             if(k==-1||t[j]==t[k])   //k=-1进入这个循环是为了整体向后移;
14                 {
15                 ++k;         k实际上也是记录了相同的个数;
16                 ++j;
17                 
18                     next1[j] = k;   找到next数组;
19         
20                 }
21                else 
22                 k = next1[k];    //不相同则更新k;
23             }
24 
25         }

现在会了next数组,我们则可以进行模式匹配了;

利用上面求的next数组来进行模式匹配;过程原理和上面画的图是一模一样的;

代码如下:

 

 

 1     int  kmp(string s,string t,int sszie,int tsize)
 2         {
 3             int j = 0;
 4             int i = 0;
 5             while(i<ssize&&j<tsize)
 6         {
 7             
 8                 if(j==-1||s[i]==t[j])  j=-1是为了调节到跳无可跳时,整体向后移;
 9             {
10                 i++;      //匹配整体向前移;
11                 j++;
12                 
13             }
14             else 
15             {
16                  j = next1[j];    不断跳next数组;
17             } 
18 
19             
20         }
21             
22             
23             if(j==tsize)
24             {
25                 return  i-j+1;  //返回模式串在主串的第一个下标;
26             }
27             else return -1//不匹配,则返回-1;
28         }

所以这道题的完整代码如下:


代码如下:
 1 #include<iostream>
 2 #include<string.h>
 3 using namespace std ;
 4 
 5 string s;
 6 string t;
 7 int ssize;
 8 int tsize;
 9 int next1[2000000];
10         void nextsz(string t,int tsize)
11         {
12             next1[0] = -1;
13             int k = -1;
14             int j = 0 ;
15             while(j < tsize-1)
16             {
17             if(k==-1||t[j]==t[k])
18                 {
19                 ++k;
20                 ++j;
21                 
22                     next1[j] = k;
23         
24                 }
25                else 
26                 k = next1[k];
27             }
28 
29         }
30         
31         int  kmp(string s,string t,int sszie,int tsize)
32         {
33             int j = 0;
34             int i = 0;
35             while(i<ssize&&j<tsize)
36         {
37             
38                 if(j==-1||s[i]==t[j])
39             {
40                 i++;
41                 j++;
42                 
43             }
44             else 
45             {
46                  j = next1[j];
47             }
48 
49             
50         }
51             
52             
53             if(j==tsize)
54             {
55                 return  i-j+1;
56             }
57             else return 0;
58         }
59         
60         
61 int main()
62 {
63     cin>>s;
64     cin>>t;
65 
66     ssize =  s.size();
67     tsize = t.size();
68         nextsz(t,tsize);
69         cout<<kmp(s,t,ssize,tsize)<<endl;
70 
71         
72     
73 }

 




以上是关于(原创)数据结构之利用KMP算法解决串的模式匹配问题的主要内容,如果未能解决你的问题,请参考以下文章

串的模式匹配算法之kmp

(王道408考研数据结构)第四章串-第二节:串的模式匹配算法(朴素和KMP)

第四章:2.串 -- 串的模式匹配算法(KMP)

串串的模式匹配算法(子串查找)BF算法KMP算法

数据结构-串的模式匹配

第四章学习小结