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的边拿出来, 就是关键路径了。