Summary1 常见算法模版

[TOC]

算法模版

字符串-整数表达式求值

#include stack, unordered_map
stack<char> op; //操作符栈
stack<int> num; //数字栈
unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'x', 2}, {'/', 2}}; //定义运算符优先级
void eval() { // 计算函数
    int b = num.top(); num.pop();
    int a = num.top(); num.pop();
    char c = op.top(); op.pop();
    int x;
    if (c == '+') x = a + b;
    else if (c == '-') x = a - b;
    else if (c == 'x') x = a * b; // 乘法符号区分'x'和'*'
    else x = a / b;
    num.push(x);
}
int main() {
    int T; cin >> T;
    while (T--) {
        string s; cin >> s;
        op = stack<char>();
        num = stack<int>();
        for (int i = 0; i < s.size(); i++) {
            if (isdigit(s[i])) { //是数,压入数字栈
                int j = i, x = 0;
                while (isdigit(s[j]) && j < s.size()) x = x * 10 + s[j++] - '0';
                num.push(x);
                i = j - 1;
            } else if (s[i] == '(') { //是左括号,压入操作符栈
                op.push(s[i]);
            } else if (s[i] == ')') {//是右括号,计算前面的数直到遇到左括号为止
                while (op.top() != '(') eval();
                op.pop(); //弹出左括号
            } else { // 是操作符
                // 栈顶操作符优先级大于等于当前操作符优先级,则要先计算
                while (op.size() && op.top() != '(' && pr[op.top()] >= pr[s[i]]) eval();
                op.push(s[i]);
            }
        }
        while (op.size()) eval(); // 对最后剩下的内容再进行最后一次运算
        printf("%d\n", num.top()); // 栈顶即为最后的值
    }
    return 0;
}

字符串-浮点数表达式求值

#include stack, unordered_map
stack<char> op;//存放运算符
stack<double> num;//存放浮点数
unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; //定义运算符优先级
void eval() { //运算
    double b = num.top(); num.pop();
    double a = num.top(); num.pop();
    char c = op.top(); op.pop();
    double x;
    if (c == '+') x = a+b;
    else if (c == '-') x = a-b;
    else if (c == '*') x = a * b;
    else x = a/b;
    num.push(x);
}
int main() {
    string s; cin>>s; // (3.34*78-72.3)*54.7-82.4 中间没空格,中间用空格需要特判
    for (int i = 0; i < s.length(); ++i) {
        if (isdigit(s[i])) {
            int j = i, k = 0;double x = 0;
            while (isdigit(s[j]) && j < s.size()) x = x * 10 + s[j++] - '0';
            if (s[j] == '.') {
                j++;
                while (isdigit(s[j]) && j < s.size()) x = x + pow(0.1, ++k) * (s[j++] - '0');
            }
            num.push(x);
            i = j-1;
        } else if (s[i] == '(') {
            op.push(s[i]);
        } else if (s[i] == ')') {
            if (!op.empty() && op.top() != '(') eval();
            op.pop();
        } else {
            if (op.empty()) {
                op.push(s[i]);
            } else if (pr[s[i]] > pr[op.top()]) {
                op.push(s[i]);
            } else if (pr[s[i]] <= pr[op.top()] && op.top() != '(') {
                if (!op.empty() && pr[s[i]] <= pr[op.top()] && op.top() != '(') eval();
                op.push(s[i]);
            } else if (op.top() == '(') {
                op.push(s[i]);
            }
        }
    }
    while (!op.empty()) eval();
    cout << num.top() << endl;
    return 0;
}

字符串匹配-KMP-单字符串匹配

int next1[maxn];
char s1[maxn], s2[maxn];

inline void get_next() {//求出next数组 next数组是从 S[0到i-1]前子串 的前缀后缀最大值
    int t1 = 0, t2;
    next1[0] = t2 = -1;
    while (t1 < len2)
        if (t2 == -1 || s2[t1] == s2[t2]) next1[++t1] = ++t2; //类似于KMP的匹配
        else t2 = next1[t2];//失配
}

inline void KMP() { //KMP
    int t1 = 0, t2 = 0;//从0位开始匹配
    while (t1 < len1) { //临界值
        if (t2 == -1 || s1[t1] == s2[t2]) t1++, t2++;//匹配成功,继续
        else t2 = next1[t2]; //失配
        if (t2 == len2) printf("%d\n", t1 - len2 + 1), t2 = next1[t2];//t2==lenn2时,匹配成功;t1-len2+1即为第一个字母的位置
    } //匹配成功后,t2置为next[t2]
}

int main() {
    scanf("%s%s", s1, s2);
    len1 = strlen(s1); len2 = strlen(s2);
    get_next();
    KMP(); // 遍历,得到所有的匹配位点
    for (int i = 1; i <= len2; ++i) printf("%d ", next1[i]);//输出next数组
    return 0;
}

字符串匹配-AC自动机-多字符串匹配

queue<int>q;
struct Aho_Corasick_Automaton{
    int c[N][26],val[N],fail[N],cnt;
    void ins(char *s){
        int len=strlen(s);int now=0;
        for(int i=0;i<len;i++){
            int v=s[i]-'a';
            if(!c[now][v])c[now][v]=++cnt;
            now=c[now][v];
        }
        val[now]++;
    }
    void build(){
        for(int i=0;i<26;i++)if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<26;i++)
            if(c[u][i])fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
            else c[u][i]=c[fail[u]][i];
        }
    }
    int query(char *s){
        int len=strlen(s);int now=0,ans=0;
        for(int i=0;i<len;i++){
            now=c[now][s[i]-'a'];
            for(int t=now;t&&~val[t];t=fail[t])ans+=val[t],val[t]=-1;
        }
        return ans;
    }
}AC;
int n;char p[1000005];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%s",p),AC.ins(p);
    AC.build();
    scanf("%s",p);int ans=AC.query(p);
    printf("%d\n",ans);
    return 0;
}

二分-整数、小数

bool check(int tg) {
  if() return true;
  return false;
}
// 整数-初始化二分的左右边界以及中间索引和值 l, r, mid, ans
int l = 0, r = maxx, mid;
while (l<=r) {
  mid = (l+r)>>1;
  int tm = 0;
  if (check(mid)) l = mid+1;
  else r = mid;
}
// 小数-初始化二分的左右边界以及中间索引和值 l, r, mid, ans
double l = 0, r = maxx, mid;
while (r-l>1e-3) { // 最小误差范围边界
  mid = (l+r)/2;
  int tm = 0;
  if (check(mid)) l = mid;
  else r = mid;
}

最小生成树

const int maxn = 1e3 + 9;
const int maxm = 1e5 + 9;
int n, m, fa[maxn], cnt=0, ans=0;
struct node {
    int x, y, c;
    bool operator<(const node nd) const { return c < nd.c; }
} e[maxm];
int findfa(int x) {return fa[x] == x ? x : fa[x] = findfa(fa[x]);} // 判断点属于哪一集合
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++i) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].c);
    sort(e, e + m); // 按照边权排序
    for (int i = 1; i <= n; ++i) fa[i] = i; // 初始化并查集fa数组
    for (int i = 0; i < m; ++i) { // 开始遍历所有边(按照边权排序)
        if (findfa(e[i].x) != findfa(e[i].y)) ans+=e[i].c, cnt++, fa[findfa(e[i].x)] = findfa(e[i].y); // 不是同一集合,代表这个边投入使用
        if (cnt == n-1) break; // 总投入使用边数等于节点个数-1.
    }
    printf("%d\n", ans);
    return 0;
}

最短路-O(n^2)


最短路-堆优化(多次入队)

struct nd {int pos, val;}
priority_queue<nd> pq;
int d[maxpoint], d1, d2; // 用于临时储存两个点到起点的距离,分别是u点到s点的距离以及v点到s点的距离
int vis[maxpoint];
int u, v; // 用于获取边的两个点的坐标,其中u是承接从优先队列中获得的点的位置,v是通过遍历邻接表获得的
void dijkstra(int s, int e) {
  for (int i = 1; i <= n; i++) d[i] = INF;
  dis[s] = 0;
  pq.push({s, dis[s]});
  while (!pq.empty()) {
    u = pq.top().pos, d1 = pq.top().val;pq.pop();
    if (!vis[u]) {
      vis[u] = 1;
      for (auto tm : G[u]) {
        v = tm.first, d2 = tm.second; // 这一块被用来刷新的d必须是从队列中拿的,d数组中的值在这一时刻还是有问题的
        if (d[v] < d1+d2) {
          d[v] = d1+d2;
          if (!vis[v]) pq.push({v, d[v]});
        }
      }
    }
  }
}

最短路-SPFA(一次入队)

  for (int i = 1; i <= n; ++i) {vis[i] = 0;d[i] = INF;}
    d[s] = 0; vis[s] = 1;
    q.push(s); // 首先只需要将起点入队
    while (!q.empty()) {
        u = q.front();q.pop();
        vis[u] = 0;
        for (int j = head[u]; j; j=e[j].nt) {
            int to = e[j].to;
            if (d[to] > d[u]+e[j].c) {
                d[to] = d[u]+e[j].c;
                if (!vis[to]) {
                    q.push(to);
                    vis[to] = 1;
                }
            }
        }
    }

最长路


树状数组

LL n, m, sum1[maxn<<2], sum2[maxn<<2], t, last, tp, ta, tb, tc;
#define lowbit(x) x&(-x)
void add(LL pos, LL k) {
    LL tem = pos*k;
    while (pos <= n) {
        sum1[pos] += k;sum2[pos] += tem;
        pos += lowbit(pos);
    }
}
void interval_add(LL l, LL r, LL k) {add(l, k);add(r+1, -k);}
LL query(LL pos) {
    LL ans = 0,tem = pos;
    while (pos >= 1) {
        ans += ((tem+1)*sum1[pos] - sum2[pos]);
        pos -= lowbit(pos);
    }
    return ans;
}
LL interval_query(LL l, LL r) {return query(r)-query(l-1);}
int main() {
    scanf("%lld%lld", &n, &m);
    for (LL i = 1; i <= n; ++i) {scanf("%lld", &t);add(i, t-last);last = t;}
    while (m--) {
        scanf("%lld", &tp);
        if (tp == 1) {scanf("%lld%lld%lld", &ta, &tb, &tc);interval_add(ta, tb, tc);}
        else {scanf("%lld%lld", &ta, &tb);printf("%lld\n", interval_query(ta, tb));}
    }
    return 0;
}

线段树(加减乘,取模)

struct node {
//左端点和右端点//加//乘
  	long long l,r; long long jia; long long ch; long long s;
} t[300100];
long long a[300100]; long long n,m,x,y,k,c,i,j; long long sum,p; //膜数 
void bt(long long l,long long r,long long u) {//建树 
    t[u].l=l;//左端点 
    t[u].r=r;//右端点 
    t[u].jia=0;
    t[u].ch=1;//初始化 
    if (l==r) {//长度为1的区间 
        t[u].s=a[l];
        return;
    }
    bt(l        ,(l+r)/2,u*2);//左子树 
    bt((l+r)/2+1,r      ,u*2+1);//右子树
    t[u].s=(t[u*2].s+t[u*2+1].s)%p;//更新当前结点的值
}
void down(LO x) {
    t[x*2].jia=(t[x*2].jia*t[x].ch+t[x].jia)%p;//加 
    t[x*2+1].jia=(t[x*2+1].jia*t[x].ch+t[x].jia)%p;
    t[x*2].ch=(t[x*2].ch*t[x].ch)%p;//乘 
    t[x*2+1].ch=(t[x*2+1].ch*t[x].ch)%p;
    t[x*2].s=(t[x].jia*(t[x*2].r-t[x*2].l+1)%p+t[x*2].s*t[x].ch%p)%p;//求和 
    t[x*2+1].s=(t[x].jia*(t[x*2+1].r-t[x*2+1].l+1)%p+t[x*2+1].s*t[x].ch%p)%p;//求和 
    t[x].jia=0;t[x].ch=1; 
}
void cheng(LO l,LO r,LO x,LO d) {
    if (l<=t[x].l&&t[x].r<=r)//全部包括 
      t[x].s=t[x].s*d%p;
        t[x].jia=t[x].jia*d%p;
        t[x].ch=t[x].ch*d%p;
    } else {
        down(x);//向下传递值 
        LO mid=(t[x].l+t[x].r)/2;
        if (l<=mid) cheng(l,r,x*2,d);//继续往下搜
        if (r>mid)cheng(l,r,x*2+1,d);
        t[x].s=(t[x*2+1].s+t[x*2].s)%p;//更新sum 
    }
    return;
}
void jf(LO l,LO r,LO x,LO d) {
    if (l<=t[x].l&&t[x].r<=r) { //全部包括 
        t[x].jia=(t[x].jia+d)%p;
        t[x].s=(t[x].s+d*(t[x].r-t[x].l+1))%p;
    } else {
        down(x);//向下传递值 
        LO mid=(t[x].l+t[x].r)/2;
        if (l<=mid) jf(l,r,x*2,d);
        if (r>mid)jf(l,r,x*2+1,d);
        t[x].s=(t[x*2+1].s+t[x*2].s)%p;//更新sum 
    }
    return;
} 
long long qh(LO l,LO r,LO x) {
    if (l<=t[x].l&&t[x].r<=r) //求得区间有这一部分,直接返回
         return t[x].s%p;
    else {
        down(x);//向下传递值 
        LO ans=0;
        LO mid=(t[x].l+t[x].r)/2;
        if (l<=mid) ans=qh(l,r,x*2)%p;
        if (r>mid)ans=ans+qh(l,r,x*2+1);
        return ans%p;
    }
}
int main() {
    scanf("%lld%lld%lld",&n,&m,&p);
    for (i=1;i<=n;i++)
    scanf("%lld",&a[i]);
    bt(1,n,1);//建树 
    for (i=1;i<=m;i++) {
        scanf("%lld",&c);//读入 
        if (c==1) { //乘
            scanf("%lld%lld%lld",&x,&y,&k);
            cheng(x,y,1,k);
        } else if (c==3) { //求和 
            scanf("%lld%lld",&x,&y);
            sum=qh(x,y,1)%p;//求和 
            printf("%lld\n",sum);//结果 
        } else if (c==2) { //加 
            scanf("%lld%lld%lld",&x,&y,&k);
            jf(x,y,1,k);
        }
    }
    return 0;
}
Posted on Feb 4, 2020