第四章---串 板子

1、KMP

板子

只返回第一处的位置KMP

class KMP {
public:
	//参数:主串、模式串
	//必须知道:主串、模式串内容,主串模式串大小
	int kmp(string source, string item) {
		vector<int> next = find_next(item);//第一步:获取Next数组
		int n = source.size();
		int n_item = item.size();
		int k = -1;//模式串判断位置指针
		for (int i = 0; i < n; i++) {//从零开始,到主串末尾
			while (k > -1 && source[i] != item[k + 1]) {//k>-1且模式串和主串不匹配才会回溯
				k = next[k];
			}
			if (source[i] == item[k + 1]) {//匹配后,模式串处理指针加一
				k++;
			}
			if (k == n_item - 1) {//结束条件,
				return i;
			}
		}
		return -1;
	}
private:
	//模式串匹配模式串
	//参数:模式串
	//必须知道:模式串内容,模式串大小
	vector<int> find_next(string item) {
		int n = item.size();
		vector<int> next(n, -1);//建立next,第一项固定-1
		int k = -1;//模式串处理位置指针
		for (int i = 1; i < n; i++) {
			while (k > -1 && item[i] != item[k + 1]) {//原理同上
				k = next[k];
			}
			if (item[i] == item[k + 1]) {
				k++;
			}
			next[i] = k;//存一下每个位置的next值
		}
		return next;
	}
};

找到所有的位置的KMP

就是在找完一次之后,重置k=-1即可,统计总共成功几次。

#include <iostream>
#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> find_next(string item) {//自己匹配自己
	int n = item.size();
	vector<int>next(n, -1);
	int k = -1;
	for (int i = 1; i < n; i++) {//第一个位置默认-1
		while (k > -1 && item[i] != item[k + 1]) {//先考虑回溯,k>-1(下界)并且匹配不上-->回溯
			k = next[k];
		}
		if (item[i] == item[k + 1]) {
			k++;
		}
		next[i] = k;
	}
	return next;
}
int KMP(string source, string item) {
	vector<int> next = find_next(item);
	int n = source.size();
	int n_item = item.size();
	int k = -1;
	int ans = -1;
	for (int i = 0; i < n; i++) {//枚举主串元素
		while (k > -1 && source[i] != item[k + 1]) {
			k = next[k];
		}
		if (source[i] == item[k + 1]) {
			k++;
		}
		if (k == n_item - 1) {
			k == -1;//这里就是,每匹配完一次,k重置,重新再次匹配,ans++
			ans += 1;
		}
	}
	return ans == -1 ? -1 : ans + 1;
}

例题

力扣:检查子树(本题还是一道经典的换角度看待二叉树,进而解决子树问题)

2、朴素方法模式匹配

板子

#include <iostream>
using namespace std;

int main() {
	//前期准备
	int size_a = 5;//主串
	int size_m = 3;//模式串
	int *a = new int[size_a];
	int *m = new int[size_m];
	int ans = -1;

	for (int i = 0; i < size_a; i++) {
		cin >> a[i];
	}
	for (int i = 0; i < size_m; i++) {
		cin >> m[i];
	}
	
	//从此处开始朴素的匹配过程
	for (int i = 0; i < (size_a - size_m + 1); i++) {//共匹配size_a - size_m +1次
		int j = 0;//每次模式串都是从头开始
		int k = i;//记录一下主串从何处开始
		for (; j < size_m; j++, k++) {//二者一起前进
			if (a[k] != m[j]) {//匹配失败,马上退出,更新 k j
				break;
			}
		}
		if (j == size_m) {//匹配成功,则j走到最后
			ans = i + 1;
			break;
		}
	}

	cout << ans;
	return 0;
}

3、滑动窗口

经典习题:力扣:424. 替换后的最长重复字符
在这里插入图片描述

思路

1、首先不考虑k,即令k=0,是最朴素滑动窗口问题
在这里插入图片描述
2、回到本题,相当于允许窗口内存在不同的元素,利用K次机会来反悔,把"和主体部分不同的元素进行更改",来使他们成为都一样的连续的串。
3、为了知道窗口内非主题元素个数,所以一定要维护一个cnt用以记录主体元素个数,(right-left+1)表示窗口大小,(right-left+1-cnt)代表需要替换的元素个数,如果>k说明此次扩张失败,left++

注意:
1、每次边界移动,都会造成窗口内元素个数变化,分别是右边界加元素,左边界减元素
2、元素变化都会造成cnt变化,注意判定
3、先元素个数变化,再边界变化

板子

class Solution {
public:
	int characterReplacement(string s, int k) {
		int word[26] = {};//动态维护窗口内元素个数
		int left = 0, right = 0, cnt = 0;//滑窗准备(左右指针),以及维护窗口内最多的元素个数
		int n = s.size();
		while (right < n) {//正确理解right指针,它指向本次要加入到窗口的元素,此时该元素未加入窗口,所以窗口大小为right-left+1
			cnt = max(cnt, ++word[s[right] - 'A']);//先变化窗口内元素个数,再看看是否会影响到cnt
			if (right - left + 1 - cnt > k) {//发现窗口大小大了
				if (word[s[left] - 'A'] == cnt) {//顺序正好反过来,看看是否影响了cnt
					cnt--;
				}
				word[s[left] - 'A']--;//在变化元素个数
				left++;//左指针右移
			}
			right++;//代表这个元素加入窗口结束&&接着指向下一个待处理的元素
		}
		return right - left;
	}
};

4、C语言操作字符串四大函数

四大函数介绍

    //字符串的赋值运算
	strcpy(str1, str2); //让str1 = str2

	//字符串的比较运算
	strcmp(str1, str2); //比较字典序,如果str1大于str2就返回正数,等于就返回0,小于就返回负数

	//字符串的拼接操作
	strcat(str1, str2); //将str2拼接到str1后面  //单词concat:合并多个字符串

	//求字符串的长度
	int len = strlen(str1);

操作思路

1、因为c语言没有string,所以只能用这四大库函数操作串。
2、在操作的时候,就把一个一个的串,当成一个一个的"数字",用这四个函数,来实现“比较,交换(复制),拼接,求长度”等功能

例题:2021软件专硕

在这里插入图片描述

#include <iostream>
#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

void function_three() {//把一个一个的串当成
	char str[10][10] = { "abc","def","ajsd","rtdfv","ajsdf","treyry" };
	bool flag = true;
	while (flag) {
		flag = false;
		for (int i = 0; i < 5; i++) {
			if (strcmp(str[i], str[i + 1]) > 0) {//串之间比较大小,用strcmp
				flag = true;
				char tmp[10] = " ";//交换两个串,使用strcpy,使用临时串保存一个
				strcpy(tmp, str[i]);
				strcpy(str[i], str[i + 1]);
				strcpy(str[i + 1], tmp);
			}
		}
	}
	return;
}

5、回文串问题

找到所有的回文串:中心扩展法

例题

力扣:647. 回文子串

思路:本质上控制变量

1、为了找到所有回文子串,一定得按照某种规律枚举才行,有序的枚举
2、任何的回文串,都存在回文中心,中心要么是一个元素,要么是两个元素
3、所以说,按顺序枚举串里面的所有元素,因为任何元素都可能是回文中心,然后考虑单中心和双中心的问题
4、然后双指针,从中心开始判断,并向两边一起扩展(注意越界判定),一旦遇到两边不同元素的情况,停止即可,枚举下一个中心

代码

class Solution {
public:
	int countSubstrings(string s) {
		int ans = 0;
		int n = s.size();
		for (int i = 0; i < n; i++) {//枚举所有回文中心
			for (int j = 0; j < 2; j++) {//0代表单中心,1代表双中心
				int l = i;//左右指针,所中心开始判断
				int r = i + j;
				//越界判定+两边一致(不一致直接停止,枚举下一个中心)
				while (l >= 0 && r < n&&s[l--] == s[r++]) {
					ans++;
				}
			}
		}
		return ans;
	}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JLU_LYM

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

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

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

打赏作者

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

抵扣说明:

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

余额充值