1. KMP算法
原理 : 获得 前缀和后缀 相同的长度 用数组保存
作用 : 求 文本串 中 模板串 的位置(没有则返回-1)
s[] 为文本串
p[] 为模板串
next[] 为前后缀相同长度
代码 :
char s[MAX],p[MAX];
int next[MAX]
void getnext(char p[])
{
int i = 0,j = -1,l = strlen(p);
next[0] = -1;
while(i < l - 1)
{
if(j == -1 || p[i] == p[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
int kmp(char s[],char p[])
{
int i = 0,j = 0,l1 = strlen(s),l2 = strlen(p),cnt;
while(i < l1 && j < l2)
{
if(s[i] == p[j])
{
i++;
j++;
}
else j = next[j];
}
if(j == l2)return i - j;
else return -1;
}
2. LCA算法
原理 : 用二维数组 fa[i][j] 保存 i 向上移动 1 << j 的祖先节点
作用 : 求共同祖先 or 点距(不成环)
代码 :
struct node
{
ll to;
ll next;
ll val;
} edge[MAX * 2]; //存边
ll n, q; //节点个数
ll head[MAX]; //head[i] 为 i 节点最后出现的 子节点 的 边序号
ll dis[MAX];
ll depth[MAX]; //存深度
ll fa[MAX][21]; //存 i 节点 向上移动 1 << j 的祖先节点
ll tol; //边个数
void add(ll from, ll to, ll val) //存边 可以加val
{
edge[tol].to = to;
edge[tol].val = val;
edge[tol].next = head[from];
head[from] = tol++;
}
void dfs(ll u, ll f)
{
fa[u][0] = f;
for (int i = head[u]; ~i; i = edge[i].next)
{
ll To = edge[i].to;
if (To != f)
{
dis[To] = dis[u] + edge[i].val;
depth[To] = depth[u] + 1;
dfs(To, u);
}
}
}
void init(ll root) //main函数里调用init初始化
{
depth[root] = 1;
dis[root] = 0;
dfs(root, -1);
for (long long i = 1; i < 20; i++)
for (long long j = 1; j <= n; j++)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
}
ll lca(ll x, ll y)
{
if (depth[x] > depth[y])
swap(x, y);
for (long long i = 0; i < 20; i++)
if ((depth[y] - depth[x]) >> i & 1)
y = fa[y][i];
if (x == y)
return x;
for (long long i = 19; i >= 0; i--)
{
if (fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
}
return fa[x][0];
}
3. RMQ算法
原理 : 二维数组p[i][j] 保存 a[i]到 a[i + (1 << j) - 1]的最值
作用 : 求区间内的最值
n 数据长度
a[] 数据
p[][] 用于保存最值
代码 :
int n,a[MAX],p[MAX][40];
void init()
{
for(int i = 1; i <= n; i++)p[i][0] = a[i];
for(int j = 1; 1 << j <= n; j++)
{
for(int i = 1; i + (1 << j) - 1 <= n; i++)
p[i][j] = max(p[i][j - 1],p[i + (1 << (j - 1))][j - 1]);
//max改成min也可以
}
}
int RMQ(int l,int r) //返回[l,r]区间的最值
{
int tem = log2(r - l + 1);
return max(p[l][tem], p[r - (1 << tem) + 1][tem]);
}
4. 并查集
代码 :
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void conbine(int x,int y){
int temp1=find(x);
int temp2=find(y);
fa[temp2]=temp1;
}
5. 线段树
原理 : tree[i].l 为左边界 tree[i].r 为右边界 tree[i].val 为区间权值(求和 or 最值)
作用 : 修改 + 查找区间权值
代码 :
struct node
{
int l, r;
ll v, lazy;
} tr[MAX * 4];
int a[MAX];
int n, q;
void update(int i) { tr[i].v = tr[i << 1].v + tr[(i << 1) + 1].v; }
void push(int i)
{
if (tr[i].lazy != 0)
{
tr[i << 1].v += tr[i].lazy * (tr[i << 1].r - tr[i << 1].l + 1);
tr[(i << 1) + 1].v += tr[i].lazy * (tr[(i << 1) + 1].r - tr[(i << 1) + 1].l + 1);
tr[i << 1].lazy += tr[i].lazy;
tr[(i << 1) + 1].lazy += tr[i].lazy;
tr[i].lazy = 0;
}
}
void build(int i, int l, int r)
{
tr[i].l = l;
tr[i].r = r;
tr[i].lazy = 0;
if (l == r)
{
tr[i].v = a[l];
return;
}
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build((i << 1) + 1, mid + 1, r);
update(i);
}
void change1(int i, int pos, int v)
{
if (tr[i].l == tr[i].r)
{
tr[i].v = v;
return;
}
if (tr[i << 1].r >= pos)
change1(i << 1, pos, v);
else
change1((i << 1) + 1, pos, v);
update(i);
}
void change2(int i, int l, int r, int v)
{
push(i);
if (tr[i].l >= l && tr[i].r <= r)
{
tr[i].v += v * (tr[i].r - tr[i].l + 1);
tr[i].lazy += v;
return;
}
if (tr[i << 1].r >= l)
change2(i << 1, l, r, v);
if (tr[(i << 1) + 1].l <= r)
change2((i << 1) + 1, l, r, v);
update(i);
}
ll search(int i, int l, int r)
{
ll ans = 0;
push(i);
if (tr[i].l >= l && tr[i].r <= r)
{
return tr[i].v;
}
if (tr[i << 1].r >= l)
ans += search(i << 1, l, r);
if (tr[(i << 1) + 1].l <= r)
ans += search((i << 1) + 1, l, r);
update(i);
return ans;
}
6. 树状数组
原理 : tree[i] = a[i - 2^k + 1] + … + a[i] 其中2^k = i&(-i); k为i二进制末尾不为零的长度
作用 : 在部分情况中 可以替代线段树
代码 :
#define lowbit(x) x&(-x);
void update(int i,int k) //定点更新
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i) //求和
{
int res = 0;
while(i > 0)
{
res += c[i];
i -= lowbit(i);
}
return res;
}
7.1 最短路 - Dij
原理 : 贪心思想 从 起点 找最短距离点(走一次 标记一次)走完 所有点
作用 : 求最短距离
vis 为标记
mp[i][j] 为题目所给 俩点 距离不通则为 INF
dis[j] 为 s(起点) 到 j 的距离
代码 (未优化) :
int vis[MAX],mp[MAX][MAX],dis[MAX];
void Dij(int s) // s为起点
{
memset(vis, 0, sizeof(vis));
vis[s] = 1;
for(int i = 1; i <= n; i++)dis[i] = mp[s][i];
int v,mn;
for(int i = 1; i <= n; i++)
{
mn = INF;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dis[j] < mn)
{
mn = dis[j];
v = j;
}
}
vis[v] = 1;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && (mn + mp[v][j]) < dis[j])dis[j] = mn + mp[v][j];
}
}
}
7.2 最短路 - Dij 优化
优化 : 优化找最小值过程
前向星 + 优先队列(重载)
代码 :
int n, q, vis[MAX], dis[MAX], head[MAX], tol;
struct edge
{
int to, next, v;
} eg[MAX * 2];
struct node
{
int id, len;
node(int id1, int len1)
{
id = id1;
len = len1;
}
bool operator<(const node &rhs) const
{
return len > rhs.len;
}
};
void add(int x, int y, int v)
{
eg[++tol].to = y;
eg[tol].v = v;
eg[tol].next = head[x];
head[x] = tol;
}
void dij(int s) // s为起点
{
for (int i = 1; i <= n; i++)
{
dis[i] = INF;
vis[i] = 0;
}
priority_queue<node> q;
dis[s] = 0;
q.push(node(s, 0));
while (!q.empty())
{
node p = q.top();
q.pop();
int v = p.id;
if (vis[v])
continue;
vis[v] = 1;
for (int i = head[v]; i != 0; i = eg[i].next)
{
int to = eg[i].to;
if (!vis[to] && dis[to] > dis[v] + eg[i].v)
{
dis[to] = dis[v] + eg[i].v;
q.push(node(to, dis[to]));
}
}
}
}
7.3 最短路-SPAF
代码 :
int head[MAX],dis[MAX],vis[MAX];
int n,tol;
struct node
{
int to, next, val;
} edge[MAX];
void add(int from,int to,int val)
{
edge[++tol].to = to;
edge[tol].val = val;
edge[tol].next = head[from];
head[from] = tol;
}
void SPAF(int x)
{
memset(dis, INF, sizeof(dis));
memset(vis, 0, sizeof(vis)); //初始化两个数组
queue<int> q;
q.push(x);
dis[x] = 0;
vis[x] = 1;
while(!q.empty())
{
int now = q.front();
q.pop();
vis[now] = 1;
for (int i = head[now]; i != 0;i=edge[i].next)
{
if(dis[edge[i].to] > dis[now] + edge[i].val)
{
dis[edge[i].to] = dis[now] + edge[i].val;
if(!vis[edge[i].to])
{
q.push(vis[edge[i].to]);
vis[edge[i].to] = 1;
}
}
}
}
}
8. 最小生成树 - Kruskal算法
原理 : 连接n个点只需要n - 1条边 + m条边的长度排序
作用 : 求连接n个点的最小路径和
代码 :
struct node
{
int x, y, l;//x,y为两点 l为连接的长度
} edge[MAX];
int cmp(node a,node b)
{
return a.l < b.l;
}
int find(int x)
{
if(x != fa[x])
return fa[x] = find(fa[x]);
else
return x;
}
int main()
{
int n, m, k = 0, ans = 0;
for (int i = 1; i <= n; i++)fa[i] = i;//初始化数组
sort(edge, edge + m, cmp);
for (int i = 0; i < m; i++)
{
int a = find(edge[i].x);
int b = find(edge[i].y);
if(a != b)
{
ans += edge[i].l;
fa[a] = b; //连接两点
k++;
}
if(k == n - 1)break;
}
if(k == n - 1)//ans就是路径长
else //不存在
}