数据结构 第四章串 习题心得

本文深入探讨了KMP算法的原理,包括最大前后缀匹配和优化特征向量nextval的计算方法,并通过实例解析了KMP算法在字符串匹配中的应用。同时,提供了C语言实现的子串替换函数StrStuff及KMP字符串匹配模板代码,强调了指针无需回溯的特点和注意事项。
摘要由CSDN通过智能技术生成

判断题

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jmu xzh_0618

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值