字符串模板--KMP

前言:2019.7.23,复习kmp算法.


 1.关于字符串匹配.

Q:给定字符串a,字符串b,问a是否在b中出现?出现多少次?出现在b的什么位置?在未学kmp之前,对于第一问我想到的是tire树查看,但2,3问tire树显然力不从心.暴力? o(n^4)的两两子串匹配?显然不行.这时就需要更高级的算法kmp.

2.kmp算法导入.

我们想想暴力究竟暴力在哪里?当我们用匹配串t去匹配主串s时一但匹配失败时,我们会从头枚举s对于t的开头再进行一次匹配.显然这样我们会对一些已经匹配好的状态重新清零,不能加以利用.例如:  对于主串S: a a a b a a a g t ... 匹配串 T: a a a b a a a j k, 显然我们在第一次暴力匹配S的'g'与T的'j'失配时,第二次匹配时我们会选择从S的第二个a开始匹配,显然我们可以直接让T的前3个a与S后3个a匹配可以简化到最优枚举,kmp就利用这一点优于暴力.

3.kmp算法.

1.nxt 数组 .

我们定义nxt[ i ] = k表示字符串t[ 0 ]..t[ k-1]与t[ i -k ]..t[ i-1 ] 一一匹配,其中k为所有可满足字符串前缀等于后缀中长度中的最大长度.怎么求?显然nxt[ 0 ] nxt[ 1 ] nxt[ 2 ]...nxt[ i] nxt[i+1]存在关系.我们设nxt[ i ] = g ,即为t[ 0 ]...t [ g-1] 与 t [ i-g ]..t[ i-1]一一匹配,当s[ g ]==s[ i ]时,显然nxt[ i+1 ]=g+1,当这时s[ g ] ! = s[ i ]时,我们将g = nxt[ g ] 就可以得到此时第二长的最长前缀等于后缀的长度 g' ( 证明可先假设 g' 为此时第二长的最长前缀等于后缀,再画图反证. ),再看s[ g ']是否等于 s [ i ] ? 等于 则 s[ i+1]=s [ g'],否则就 g''=nxt[ g' ],得到第三长的最长前缀等于后缀的长度 g'',类推即可.最坏情况我们推至g=nxt[0]=-1(初始化) , 直接退出循环,s[i+1 ]=g+1=0,即不存在.code:

 

 1 void getnext()
 2 {
 3     int k=-1,i=0;
 4     nxt[0]=-1;
 5     while(i<lent){
 6         while(k>=0&&t[k]!=t[i])
 7             k=nxt[k];
 8         i++,k++;
 9         nxt[i]=k;
10     }
11 }

 

2.利用nxt数组优化字符串匹配.对于主串S,匹配串T,用 j 表示在S上的位置,用 i 表示在 T 上的位置.用两层while,第一层while限制j<lens,第二层while尝试用 i 去匹配 j,当匹配成功时++i,++j,未匹配成功时就令i = nxt[ i ], 以减小匹配带来的不必要的枚举.当 i = =lent时则代表匹配成功,可令i = nxt [ i ] 进行反复匹配.这样我们知道了 T 在 S 上出现了多少次即出现的位置. code:

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 #define e exit(0)
 6 #define R register
 7 const int maxn=1e6+10;
 8 char s[maxn],t[maxn];
 9 int lens,lent,deep,nxt[maxn],pos[maxn];
10 void getnext()
11 {
12     int k=-1,i=0;
13     nxt[0]=-1;
14     while(i<lent){
15         while(k>=0&&t[k]!=t[i])
16             k=nxt[k];
17         i++,k++;
18         nxt[i]=k;
19     }
20 }
21 void kmp()
22 {
23     int i=0,j=0;
24     while(j<lens){
25         while(i>=0&&t[i]!=s[j])
26             i=nxt[i];
27         i++,j++;
28         if(i==lent){
29             pos[++deep]=j-lent+1;
30         }
31     }
32 }
33 int main()
34 {
35 //    freopen("s.in","r",stdin);
36 //    freopen("s.out","w",stdout);
37     scanf("%s",s),scanf("%s",t);
38     lens=strlen(s),lent=strlen(t);
39     getnext();
40     kmp();
41     for(R int i=1;i<=deep;++i)
42         printf("%d\n",pos[i]);
43     for(R int i=1;i<=lent;++i)
44         printf("%d ",nxt[i]);
45     return 0;
46 }
posted @ 2019-07-24 14:58  xqyxqy  阅读(146)  评论(0编辑  收藏  举报