算法模板

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 //不存在
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值