数据结构—— 第四章串+第六章图(关键路径)

KMP算法

KMP算法的作用是在一个已知字符串中查找子串的位置,也叫做串的模式匹配。
字符串的匹配: 暴力就是一个一个匹配,一发现错误,马上回退到开头的下一个,但是这样会比较慢。

让后移动的位数增加,减少一些不必要的判断。

这里得到一个公式:移动的位数 = 已匹配的字符数 - 对应的部分匹配值

原理就是: 从已经匹配的一段字符串的后一个字母重新开始匹配
比如下面的例子:
ab是匹配的,但是a就和c不匹配, 所以从 最后的那个a开始重新匹配。
在这里插入图片描述
看以下的图示:
在这里插入图片描述

改进: 右移位数= 已匹配的字符数- 对应的部分匹配 Move = (j - 1) - PM[ j - 1]

next[i]的值表示下标为i的字符前的字符串最长相等前后缀的长度。
在这里插入图片描述

在这里插入图片描述

KMP算法
指针i永远不会回退。
在这里插入图片描述
KMP 算法永不回退 txt 的指针 i,不走回头路(不会重复扫描 txt),而是借助 dp 数组中储存的信息把 pat 移到正确的位置继续匹配,时间复杂度只需 O(N),用空间换时间,所以我认为它是一种动态规划算法。

换一种理解方式

每次找最长的公共前后缀。
下图发现, AB是最长的公共前后缀。
所以只需要把前缀移动到后缀的位置,就是最少的匹配次数, 加快匹配的速度。
如果由多对前后缀, 选择最长的那个。
最长的这个不能包括原来的串,
在这里插入图片描述
移动后
在这里插入图片描述
每次比较其实都是,找最大公共前后缀长度。
在这里插入图片描述
在这里插入图片描述

next 数组

next[ j ]的值, k = 下标为 j 的 字符前 的字符串 最长相等前后缀 的长度 + 1
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
它的改进在于:
每当从某个起始位置开始一趟比较后,在匹配过程中出现失配,不回溯i,而是利用已经得到的部分匹配结果,
将一种假想的位置定位“指针”在模式上向右滑动尽可能远的一段距离到某个位置后,继续按规则进行下一次的比较。

#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#include <string>
typedef long long ll;
using namespace std;
//未改进的KMP算法代码实现
void get_next(int *next, char *T, int len)
{
	next[0] = -1;//-1代表没有重复子串
	int k = -1;
	for (int q = 1; q <= len; q++)
	{
		while (k > -1 && T[k+1] != T[q])//下一个元素不相等,把k向前回溯
		{
			k = next[k];
		}
		if (T[k+1] == T[q])//下一个元素相等,所以最长重复子串+1
		{
			k = k+1;
		}
		next[q] = k;//给next数组赋值
	}
}
int KMP(char *s, int len, char *p, int plen)//利用KMP算法匹配
{
	int *next = new int(plen);
	get_next(next, p, plen);
	int k = -1;
	int i = 0;
	for (; i < len; i++)
	{
 		while (k > -1 && p[k+1]!=s[i])//两串下一个字符不相等,向前回溯(效率高就是在这里,每次匹配失败,k不用直接变为0,从第一个字符开始重新匹配,而是变为最长重复子串的下一个字符,从中间开始匹配即可)。
		{
			k = next[k];
		}
		if(p[k+1] == s[i])//两个串的字符相等,k+1来匹配子串的一个字符
		{
			k++;
		}
		if (k == plen-1)//匹配成功,返回短串在长串的位置。
		{
			return i-plen+1;
		}
	}
	return -1;
}
int main()
{
	
	char a[] = "bacbababadababacambabacaddababacasdsd";
	char b[] = "ababaca";
	int m = KMP(a,strlen(a),b,strlen(b));
	printf("%d", m);
	return 0;
}

关键路径算法

在这里插入图片描述
这里可以用边表示活动,构建一个 AOE网络。
比如上图的装修工程可以构建出下图的AOE网络。我们把工程计划表示为边表示活动的网络,即 AOE网,顶点表示事件(Event),弧表示活动,权表示活动持续的时间。
最后一个有4,7,8 三个前序工程,所以指向同一个顶点。
在这里插入图片描述
对于AOE网,我们关心两个问题:
完成整项工程至少需要多少时间? — 求关键路径长度
哪些活动是影响工程进度的关键? — 求关键路径上经过的顶点(活动)

关键路径 — 路径长度最长的路径。
路径长度 — 路径上各活动持续时间之和。
确定关键路径之前,我们需要定义4个描述量(以上图为例):
ve(vj) ----- 表示 事件 vj 的最早发生时间。
vl(vj) ----- 表示 事件 vj 的最晚发生时间。
e(i) ---- 表示活动 ai的最早开始时间。
l(i) ---- 表示活动 ai (边) 的最晚开始时间。

关键活动 ---- 关键路径上的活动,即 l(i) == e(i)即(l(i) - e(i) = 0)的活动。
发现边 A 是关键的, 只有一条路径可以走,最早开始时间和最晚开始是一致的。

三 ,如何求得关键路径

那么我们如何找到满足 l(i) == e(i)的关键活动?

在这里插入图片描述
举例:
V1-> V4 有两条路径
这两条路径可以同时进行的, 则V4开始的最早时间, 是 最长的完工时间。 从图中可知, 最早开始时间是max{5, 6}=6
在这里插入图片描述
第一步: 用 拓扑 排序求 ve(), 一步一步往后计算。最终得到最大的那个长度时间。
得到最终的最早时间 : V6 最早开始的时间是 8 。
第二步: 用 逆拓扑 排序求 vl() , 从 v6开始计算,最晚开始的时间。这里 最晚开始时间v6就是 8 , 然后逆推 V5 就是 8-1, 为7.
V4 最晚时间为 6, V3 最晚时间是:min {8-3,8-6} = 2 , V2 最晚时间是 8-4= 4, V1 最晚时间就是0 ,
第三步: 弧最早开始时间 e()等于该弧的起点的顶点的 ve() , (比较容易懂,就是弧最早开始时间,那么顶点就也最早开始时间)
第四步:用下面图的公式进行计算
可以计算得到 l(i) 数 组。
比如 a1 终点的 vl(2)=4减去 该弧的时间长度(3)= 1 , 所以 l(1)最晚开始时间是1。
在这里插入图片描述
最后得到 l(i)- e(i)=0的边拿出来, 就是关键路径了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值