判断题
1、KMP算法的最大特点是指示主串的指针无须回溯。
答案:(正确)
选择题
1、令s="abaabc",则它的特征向量next函数值和优化特征向量nextval函数值为(下标从0开始)
A.next={-1,0,0,1,1,2}, nextval={-1,0,-1,1,0,2}
B.next={0,1,1,1,2,2}, nextval={0,1,1,0,1,1}
C.next={-1,0,0,1,2,1}, nextval={-1,0,-1,1,0,1}
D.next={-1,0,1,1,1,2}, nextval={-1,0,-1,1,0,1}
答案:A
解析:本章节的核心即为kmp算法,要学会理解其原理和源代码且使用。
next[i]数组求法即判断前后缀最大字符串,一般判断到最后一个字符停止,有可能题目有特别指出要判断到最后一个字符的下一位也是可以的。
nextval[i]数组求法:
(1) j = 0,nextval[j] = -1;
(2) j>0, P [j] == P next[j] , nextval[j] = nextval [ next[j] ];
P [j] != P next[j] , nextval[j] = next[j];
(前面P指的是对应下标字符之间的比较)
(此方法适用于做选择题和判断题,对于编程代码的实现是不适用的)
2、已知字符串S为“abaabaabacacaabaabcc”,模式串t为“abaabc”,采用KMP算法进行匹配,第一次出现“失配”(s[i] != t[j])时,i=j=5,则下次开始匹配时,i和j的值分别是
A.i=1,j=0
B.i=5,j=0
C.i=5,j=2
D.i=6,j=2
答案:C
解析:注意点:指示主串的指针无需回溯,kmp中的 j = next[j] 使字串中的指针回溯。
函数题
6-5 替换子串*
分数 10
// 替换子串 char* StrStuff(char *dst, int idx, int len, const char *src);
说明:
dst
为指示目的串起始地址的指针,idx
为待删除子串的起始位置(下标),len
为待删除子串的长度,src
为指示待插入源串的起始地址的指针。函数将目的串dst
中从下标idx
处开始、长度为len
的子串替换为源串src
,函数值为dst
。要求:函数能容错运行。若
len
不正确,则自动修正。若idx
不正确,则不作任何处理。裁判程序:
#include <stdio.h>
// 替换子串
char* StrStuff(char *dst, int idx, int len, const char *src);int main()
{
char a[1024], b[1024];
int i, n;
gets(a);
scanf("%d%d%*c", &i, &n);
gets(b);
StrStuff(a, i, n, b);
puts(a);
return 0;
}/* 你提交的代码将被嵌在这里 */
输入样例1
abcd 1 2 efg
输出样例1
aefgd
输入样例2
abcd 1 5 (注:按3处理) efgh
输出样例2
aefgh
输入样例3
abcd 2 -5 (注:按0处理) efg
输出样例3
abefgcd
输入样例4
abcd 8 3 efg
输出样例4
abcd
编写代码:
char* StrStuff(char *dst, int idx, int len, const char *src)
{
int n=0,j=0,l=0;
char *p = NULL;
char c[1024];
for(int i=0;dst[i]!='\0';i++)
n++;
if(idx<0||idx>n) return dst; //此处可以等于n
if(len<=0) len = 0;
else if(len>n-idx) len = n-idx;
for(int i=0;i<=n;i++)
{
if(i<idx)
{
c[j] = dst[i];
j++;
}else if(i>=idx)
{
p = &dst[i];
if(l==len)
{
for(int k=0;src[k]!='\0';k++)
{
c[j] = src[k];
j++;
}
break;
}
l++;
}
}
while(*p!='\0')
{
c[j] = *p;
p++;
j++;
}
c[j] = '\0';
int y=0;
for(int i=0;c[i]!='\0';i++)
{
dst[y] = c[i];
y++;
}
dst[y] = '\0';
return dst;
}
注意点:其实还有特殊情况:
测试用例:abcd
4 2
efg
预期结果:abcdefg
在C语言中的字符串是以数组实现的,即在最后一个字符后面还有一个数组元素'\0'来判断字符串结束,例:字符串abcd,其str[4] = '\0',c语言没有字符串对象,只能用\0判断结束。
而面向对象的编程Python和c++,字符串长度存储在类里面,判断程序就是以长度判断结束(面向对象的思想)。
编程题
7-2 【模板】KMP字符串匹配
分数 20
给出两个字符串text和pattern,其中pattern为text的子串,求出pattern在text中所有出现的位置。
为了减少骗分的情况,接下来还要输出子串的前缀数组next。
输入格式:
第一行为一个字符串,即为text。
第二行为一个字符串,即为pattern。
输出格式:
若干行,每行包含一个整数,表示pattern在text中出现的位置。
接下来1行,包括length(pattern)个整数,表示前缀数组next[i]的值,数据间以一个空格分隔,行尾无多余空格。
输入样例:
ABABABC ABA
输出样例:
1 3 0 0 1
样例说明:
代码:
#include <iostream>
#include <string>
using namespace std;
void getnext(int next[], string s) //求得字串的next[i]数组
{
int i = 0, j = -1;
next[0] = -1;
while (i < s.length())
{
if (j == -1 || s[i] == s[j])
{
i++; j++;
next[i] = j;
}
else
j = next[j];
}
}
void kmp(string t, string s)
{
int next[1000000], i = 0, j = 0, n; //此处数组开辟空间int next[int (1e6)]大小合适
getnext(next, s);
while (i < t.length())
{
if (j == -1 || t[i] == s[j])
{
i++;
j++;
}
else
j = next[j];
if (j == s.length()) //根据题目所要求写此需要的功能代码
{
n = i - j + 1;
cout << n << endl;
}
}
for (int i = 1; i <= s.length(); i++)
{
if (i < s.length())
cout << next[i] << " ";
else cout << next[i]<<endl;
}
}
int main()
{
string t, s;
cin >> t; cin >> s;
kmp(t, s);
return 0;
}
kmp算法在编程题中的使用基本是照搬使用的,不同的点依据题目而稍微变动,例如求主串与子串第一次匹配的位置:
if(j == s.length())
return (i-j+1);
或者求出所有匹配成功的下标位置,即if(j == s.length())此条件成立下,每次都输出(i-j+1)。
kmp代码的核心框架:(此处以返回主串和字串第一次匹配的首个字符的位置为例)
#include <iostream>
#include <string>
using namespace std;
void getnext(int next[],string s)
{
int i=0,j=-1;
next[i] = -1;
while(i<s.length())
{
if(j==-1||s[i]==s[j])
{
i++;
j++;
next[i] = j;
}
else
j = next[j];
}
}
int kmp(string t,string s)
{
int next[100000],i = 0, j = 0;
getnext(next,s);
while(i<t.length())
{
if(j==-1||t[i]==s[j])
{
i++;
j++;
if(j==s.length())
return (i-j+1);
}
else
j = next[j];
}
return (-1);
}
int main()
{
string t,s;
cin>>t;
cin>>s;
cout<<kmp(t,s);
return 0;
}