实验七 串

DS串应用--KMP算法

输入

第一个输入t,表示有t个实例

第二行输入第1个实例的主串,第三行输入第1个实例的模式串

以此类推

输出

第一行输出第1个实例的模式串的next值

第二行输出第1个实例的匹配位置,位置从1开始计算,如果匹配成功输出位置,匹配失败输出0

以此类推

输入样例1

3
qwertyuiop
tyu
aabbccdd
ccc
aaaabababac
abac
输出样例1

-1 0 0 
5
-1 0 1 
0
-1 0 0 1 
8
 

#include<iostream>
#include<string>
using namespace std;
//KMP算法的核心是利用匹配失败后的信息,
//尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
//具体实现就是通过一个next()函数实现,
class mystring
{
private:
	string mainstr;
	int size;
	//求得next数组
	void getnext(string p, int next[])
	{
		int j = 0, k = -1;   //getbext中 k表示最大相等长度 初值设为-1 当前字符没有前后相等字串
		next[0] = -1;//next默认值为-1  因为一个字符串无最大前后相等字串
		int len = p.length();
		while (j < len - 1) //找next j最大len-2
		{
			if (k == -1 || p[k] == p[j] ) //k为-1 或者 匹配字符
			{
				j++; k++;     //检查下一个字符 最大相等长度+1
				next[j] = k;   //next[j] = k, 将最大相等长度存入next数组
			}
			else
			{
				k = next[k]; 
				//匹配失败位置,找到他前面的字符串的最大前后相等子串长度K
			}
		}
	}
	//kmp算法 找到匹配字串的首字符
	int kmpfind(string p, int pos, int next[]) //从pos位置开始查找字串
	{
		int  j= pos, k = 0;     //kmpfind中 k表示模式串当前正在匹配的位置 从0开始
		int len = p.length(); //子串长度
		while (j < size && k < len)   //pos位置要少于主串长度 
		{
			if (k == -1 || p[k] == mainstr[j])      //匹配主串第pos位置的字符
			{
				j++; k++;     //匹配下一个字符 
			}
			else
			{
				k = next[k];  
			}

		}
		if (k == len)
			return j - len + 1;  //子串走到尽头,返回开始匹配时的位置
		return 0;    //不匹配标志
	}

public:
	mystring()
	{
		size = 0;
		mainstr = "";

	}
	~mystring()
	{
		size = 0;
		mainstr = "";
	}
	void setval(string sp)
	{
		mainstr = sp;
		size = mainstr.length();
	
	}
	int kmpfindsubstr(string p, int pos)
	{
		int l = p.length();
		int* next = new int[l];
		getnext(p, next);             //得到next值并输出
		for (int i = 0; i < l; i++)
		{
			cout << next[i] << ' ';
		}
		cout << endl;
		int v = -1;
		v = kmpfind(p, pos, next);
		delete[]next;
		return v;

	}
};

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		mystring str;
		string sp, p;
		int pos = 0;
		cin >> sp >> p; //输入主串字串
		str.setval(sp);
		cout << str.kmpfindsubstr(p, pos) << endl; //从pos位置开始查找字串
	}
}

DS串应用--串替换

题目描述

给出主串、模式串、替换串,用KMP算法找出模式串在主串的位置,然后用替换串的字符替换掉模式串

本题只考虑一处替换的情况,如果你想做的完美一些,能够实现多处替换那

可能需要考虑模式串和替换串长度不一致的情况

输入

第一个输入t,表示有t个实例

第二行输入第1个实例的主串,第三行输入第1个实例的模式串,第四行输入第1个实例的替换串

以此类推

输出

第一行输出第1个实例的主串

第二行输出第1个实例的主串替换后结果,如果没有发生替换就输出主串原来的内容。

以此类推

输入样例1

3
aabbccdd
bb
ff
aaabbbccc
ddd
eee
abcdef
abc
ccccc
输出样例1

aabbccdd
aaffccdd
aaabbbccc
aaabbbccc
abcdef
cccccdef
 

#include<iostream>
#include<string>
using namespace std;
class myString
{
private:
	string mainstr;
	int size;
	void getnext(string p, int next[])
	{
		int j = 0, k = -1;
		next[0] = -1;
		int len = p.length();
		while (j < len - 1)
		{
			if (k == -1 || p[j] == p[k])
			{
				j++; k++;
				next[j] = k;
			}
			else
			{
				k = next[k];
			}
		}
	}
	int kmpfind(string p, int pos, int next[]) //从pos位置开始查找字串
	{
		int  j = pos, k = 0;     //kmpfind中 k表示模式串当前正在匹配的位置 从0开始
		int len = p.length(); //子串长度
		while (j < size && k < len)   //pos位置要少于主串长度 
		{
			if (k == -1 || p[k] == mainstr[j])      //匹配主串第pos位置的字符
			{
				j++; k++;     //匹配下一个字符 
			}
			else
			{
				k = next[k];
			}

		}
		if (k == len)
			return j - len ;  //子串走到尽头,返回开始匹配时的位置
		return -1;    //不匹配标志
	}
public:
	myString()
	{
		size = 0;
		mainstr = "";
	}
	~myString()
	{
		size = 0;
		mainstr = "";
	}
	void setval(string sp)
	{
		mainstr = sp;
		size = mainstr.length();
	}
	int kmpfind1(string p, int pos)
	{
		int L = p.length();
		int* next = new int[L];
		getnext(p, next);
		int v = -1;
		v = kmpfind(p, pos, next);
		delete[] next;
		return v;
	}
};

int main()
{
	int t;
	cin >> t;
	string sp, p, change;
	while (t--)
	{
		myString str;
		cin >> sp >> p >> change;
		str.setval(sp);
		cout << sp << endl;
		int v = str.kmpfind1(p, 0);
		int l = p.length();
		if (v >= 0)
		{
			sp.replace(v, l, change); //用change替换指定字符串从起始位置v开始长度为l的字符
		}
		cout << sp << endl;
	}
	return 0;
}

 串应用- 计算一个串的最长的真前后缀

题目描述

给定一个串,如ABCDAB,则 ABCDAB的真前缀有:{ A, AB,ABC, ABCD, ABCDA } ABCDAB的真后缀有:{ B, AB,DAB, CDAB, BCDAB } 因此,该串的真前缀和真后缀中最长的相等串为AB,我们称之为该串的“最长的真前后缀”。 试实现一个函数string matched_Prefix_Postfix(string str),得到输入串str的最长的真前后缀。若不存在最长的真前后缀则输出empty

输入

第1行:串的个数 n 第2行到第n+1行:n个字符串

输出

n个最长的真前后缀,若不存在最长的真前后缀则输出empty。

输入样例1

6
a
ab
abc
abcd
abcda
abcdab

输出样例1

empty
empty
empty
empty
a
ab

#include<iostream>
#include<string>
using namespace std;

int *getnext(string str)
{
	int j = 0, k = -1;
	int len = str.length();
	int* next = new int[len + 1];
	next[0] = -1;
	while (j < len)
	{
		if (k == -1 || str[k] == str[j])
		{
			j++; k++;
			next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}
	return next;
}

void matched(string str)
{
	int* next = getnext(str);
	int len = str.length();
	int max= next[len];  //查看next数组的最后一个元素(最大公共前后缀) 赋值给ans

	if (max <= 0)
	{
		cout << "empty" << endl;
	}
	else
	{
		cout << str.substr(0, max) << endl;//截取从0到长度max的字符
	}

}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		string str;
		cin >> str;
		matched(str);
	}
}


 

DS串应用—最长重复子串

题目描述

求串的最长重复子串长度(子串不重叠)。例如:abcaefabcabc的最长重复子串是串abca,长度为4。

输入

测试次数t

t个测试串

输出

对每个测试串,输出最长重复子串长度,若没有重复子串,输出-1.

输入样例1

3
abcaefabcabc
0szu0123szu1
szuabcefg
输出样例1

4
3
-1

#include<iostream>
#include<cstring>
using namespace std;
int* getnext(string str) //返回指针而不是数组
{
	int j = 0, k = -1;
	int len = str.length();
	int* next = new int[len + 1];     //指针new出来  长度+1
	next[0] = -1;
	while (j < len)  //指针的话就是<len
	{
		if (k == -1 || (str[j] == str[k]))
		{
			j++; k++;
			next[j] = k;
		}
		else {
			k = next[k];
		}
	}
	return next;
}
int rep(string str) {
	int len = str.length();
	int max = 0;
	string* fun = new string[len + 1];  //存储从字符串中截取的子串
	for (int i = 0; i < len; i++)
	{
		fun[i] = str.substr(i, len);
	}
	for (int i = 0; i < len; i++)  //遍历fun中的每个字串 获取每个字串的next值
	{
		int* next = getnext(fun[i]);
		int len = fun[i].length();
		//遍历next数组 找到最大的next
		for (int j = 0; j < len; j++)
		{
			if (max < next[j])
			{
				max = next[j];
			}
		}
	}
	if (max == 0) {
		return -1;
	}
	else if (max >= str.length() / 2)//如果最大长度超过了匹配长度的一半 意味着字符串前半部分和后半部分有重叠部分
	{
		return max / 2;       //避免重复计算 /2  表示重叠部分长度
	}
	else {
		return max;
	}
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		string str;
		cin >> str;
		cout << rep(str) << endl;
	}
	return 0;
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值