这道题目早就做了,然而当初没写题解,今天补上,作为第一篇OJ题解~
这题是一道模板题,说白了,就是考察你会不会KMP(看毛片)算法。当然像我这种菜鸡小白,做这题之前根本不会KMP,看到匹配吗,那就两个循环暴力匹配嘛~简单!然后就一遍又一遍的RTE、TLE或WA。然后,整个人就处于崩溃边缘(;´༎ຶД༎ຶ`)!求助同桌之后,得知暴力的话肯定会超过内存和时间限制,所以照同桌说的,去学了KMP。
学习
♂资料:从头到尾彻底理解KMP(2014年8月22日版)
这是一篇CSDN博客,写得非常非常好,我这种菜鸡看完之后都会了(虽然花了一整天55555,笨呐!)。本文以下的代码段也来自这篇博客。
做完题之后,再反复思考代码,自己又有了一些感悟。
首先,对于KMP算法,要得到一个模式串(通俗的说就是较长的那个)的next数组,求next数组的代码是:
void GetNext(char p[],int next[]){
int plen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while(j < plen){
if(k == -1 || p[j] == p[k]){
++k;
++j;
next[j] = k;
}else{
k = next[k];
}
}
}
next数组中元素next[ i ](next[ 0 ] = -1人为规定除外)的含义就是:模式串中第 i 位字符之前的最长border的长度,或者说,前缀能和后缀能匹配(相同)的最大长度(特别说明的是这种匹配允许前缀和后缀部分重叠)。
其中,if 部分就是为 next数组 赋值的过程,只有当 k为-1 或 p[ j ](后缀部分)和 p[ k ](前缀部分)匹配时,才能求得next数组的值。而else干的事情就是,当 p[ j ] 和 p[ k ]不匹配时,返回到k前面找k之前的子串中的最大匹配,不断递归,直到找到,或者回到 k = next [ 0 ] = -1时结束。(这么说都抽象,自己写一个字符串,按代码的步骤自己求一下next数组,感受一下,很快就明白什么意思了)
然后,就是利用next数组在模式串中找目标串(通俗的说就是比较短的那个)了,代码如下:
(103题目要求是返回目标串出现次数,要对那篇博客的代码稍作修改,下面贴出来的是我修改过后的代码)
int kmp(char s[],char p[]){
int i = 0,j = 0;
int slen = strlen(s),plen = strlen(p);
int next[plen];
GetNext(p, next);
while(i < slen){
if(j == plen) j = next[plen];
if(j == -1 || s[i] == p[j]){
i++;
j++;
}else{
j = next[j];
}
if(j == plen) ++cnt;
}
return cnt;
}
其中s为模式串,p为目标串。那三个 if 还是从下往上解释好了。
第三个 if 干得事情很简单,就是当目标串匹配到末尾,也就是在模式串中找到目标串了,计数变量就+1。
然后,第二个 if 就是匹配的过程了,如果找到了,就 i 和 j 都+1,即移动到下一个位置;否则,不匹配的话,就要利用目标串的next数组,找前缀中是否有匹配子串。所以整个过程中 i 始终不返回,而 j 利用next数组能做到返回到最大border的位置,不需要像暴力匹配那样,一不匹配就返回目标串的首位置,大大提升了效率。
最后,第一个 if 就是当找到一个目标串后,要继续接着找目标串,那么就不用返回到目标串得第一个位置,而是利用目标串的next数组,找到最大border的位置,并返回那里。
—————————————————————————————————————————————————————
完。根据上面两段代码,然后自己去把需要的变量、数组什么的定义一下,输入输出再写一下,就好了。最后,一个细节是,大小比较大的数组开成全局,否则会爆的......