淼淼刷力扣

引言:

本人初次尝试写博客,希望各位看官大佬多多包容
有错误希望巨巨们提出来,我一定会及时改正,谢谢大家
在自己最好的年纪,找到了未来的目标
还有1年奋斗刷题,明年去面试实习,加油!

老样子,先看看题目要求:

在这里插入图片描述
在这里插入图片描述

DFS求先跟序列,再KMP匹配

定策略

1、我们想看T树在不在S树,我现在对树没有办法,不知道咋判断,所以:我们要把树给转化,把树换一种形式,再去比对。
2、树中的点在先根遍历中得到的序列是连续的,我们不妨把先把两棵树先转化成先跟序列,即转化成了两个串,如果T树的串在S树的串,那么为true。

分析过程:

First — 求先跟序列

先跟序列的求法自不必说,但是这里有一个注意的地方,就是比如 4为根节点,5 作为左儿子,先跟序列为45,如果5为右儿子,则先跟序列依然为45,所以必须要加以区分,故插入无意义节点。

Second — KMP

KMP才是这个算法的精髓,我得到两个串之后,利用KMP看T树能不能匹配的上。
这是第一点,利用两个串的比较巧妙地转移了问题。
第二点就是重中之重的KMP书写了。
首先我们应该写出来next数组,方法如下图:

在这里插入图片描述

next数组其实本质上就是两个相等的串进行匹配,成功向前走,失败之后回溯。
下一个进入KMP书写:如果next数组书写毫无问题,那么KMP其实就是把两个相同的串换成了不同的串,和next极为相像(next为相同的俩串),具体原理就是成功匹配就继续向下走,直到小串长度-1为止,不成功的分析和我上图一致,稍微有点不同在于这次找家长(回溯)了,家长想的是如果我后一个能和主串哪一个点的值匹配上了,就可以从那个点开始继续向下了,就可以实现一次向后走多个格子的目标了,而不是小串一次只向前走一步了。若k=-1了,小串就随着大串的指针走了。

代码如下:

class Solution {
public:
	vector<int> target_one;//主串先序队列
	vector<int> target_two;//副串先序队列
	int LNULL = 1 << 20;//插入的无意义点的
	int RNULL = 1 << 20;//插入的无意义点
	bool isSubtree(TreeNode* s, TreeNode* t) {
		find_path(s);
		find_path_ano(t);
		return KMP();
	}
	//以下开始KMP操作 ,这里的K值因为需要总比用于数组寻址时少一
	vector<int> next(vector<int> item) {//这个是自己匹配自己,去看以每一个节点为结束的串前后缀多长

		vector<int> temp(item.size(), -1);//存next数组
		int len = item.size();//先求出长度,后面直接调用这个,用于加速
		int k = -1;
		for (int i = 1; i < len; i++) {
			//对每一个节点求一个next值,代表
			while (k > -1 && item[i] != item[k + 1]) {//这里k=-1,能匹配上就k++不行就直接把k值给出即可,没法回溯了
				//因为匹配不上了,所以必须回溯了
				k = temp[k];//匹配不上找家长
			}
			if (item[i] == item[k + 1]) {
				k++;//匹配上了前缀结束位置加一
			}
			temp[i] = k;//记录以每个点结尾的字串他们前缀的结束位置,便于next 和KMP中找家长回溯,因为回溯找家长就是找上一次前缀结束位置
		}
		return temp;
	}
	bool KMP() {
		vector<int> next0 = next(target_two);
		int k = -1;
		int len_s = target_one.size();//长度提前求便于加速
		int len_t = target_two.size();
		for (int i = 0; i < len_s; i++) {
			while (k > -1 && target_one[i] != target_two[k + 1]) {
				k = next0[k];//回溯找家长
			}
			if (target_one[i] == target_two[k + 1]) {
				k++;//成功记一下长度
			}
			if (k == len_t - 1) {//达到目标长度即可
				return true;
			}
		}
		return false;
	}

	void find_path(TreeNode* item) {//很简单,注意递归时函数名别写错了,为空插入无意义点
		target_one.push_back(item->val);
		if (item->left != nullptr) {
			find_path(item->left);
		}
		else {
			target_one.push_back(LNULL);
		}
		if (item->right != nullptr) {
			find_path(item->right);
		}
		else {
			target_one.push_back(RNULL);
		}
	}
	void find_path_ano(TreeNode* item) {
		target_two.push_back(item->val);
		if (item->left != nullptr) {
			find_path_ano(item->left);
		}
		else {
			target_two.push_back(LNULL);
		}
		if (item->right != nullptr) {
			find_path_ano(item->right);
		}
		else {
			target_two.push_back(RNULL);
		}
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):
在这里插入图片描述

1、经计算,该算法时间复杂度为O(∣s∣+∣t∣)
2、一定要时刻想着去转换,原来两个树之间看一个在另一个里面有没有,完全可以用同一种方法把二者转换成另一种形式在进行比较,把复杂的问题套以已知的模型,再注重一下特殊情况的处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JLU_LYM

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

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

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

打赏作者

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

抵扣说明:

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

余额充值