设为首页 加入收藏

TOP

C++算法之旅、05 基础篇 | 第二章 数据结构(三)
2023-09-09 10:25:34 】 浏览:233
Tags:
st int N = 1e6 + 10; int a[N], q[N], st = 0, ed = -1; int main() { cin.tie(0); int n, k; cin >> n >> k; for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 0; i < n; i++) { // 移动队头 if (st <= ed && i - k + 1 > q[st]) st++; // 移动队尾 while (st <= ed && a[q[ed]] >= a[i]) ed--; // ^ 先插入 q[++ed] = i; // 每次滑动输出 if (i >= k - 1) printf("%d ", a[q[st]]); } cout << endl; st = 0; ed = -1; for (int i = 0; i < n; i++) { if (st <= ed && i - k + 1 > q[st]) st++; while (st <= ed && a[q[ed]] <= a[i]) ed--; q[++ed] = i; if (i >= k - 1) printf("%d ", a[q[st]]); } return 0; }

KMP

一个人能走多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己

强烈建议看 胡凡 算法笔记 P455

KMP字符串 —— 深入浅出 next 数组

image-20230902122812982

假设下标从1开始。字符串 S[1,N],子串 P[1,M],S 指针 i-1,P 指针 j

next[j]

  • 子串中,以 j 为终点的后缀 与 从1开始的前缀相等的最长长度 x
  • \(P[1,x] = P[j-x+1,j]\)

kmp 建议看算法笔记

  1. i-1 与 j 对应字符相同;i 与 j+1 对应字符不同。此时需要把红颜色子串往后移动,为了移动最少需要 next[j]
  2. 让 j = next[j],从线2变为线3(线1红色部分 等于 线3红色部分
  3. 继续匹配 i 与 j+1,若发现不匹配,再 j = next[j] (递归进行)
  4. 当 j = m,意味着找到子串,然后 j = next[j] 继续寻找

时间复杂度计算

  • 生成next数组 \(O(m)\)
  • 字符串每个字符被遍历一次,\(O(n)\)
  • j++ 最多 n 次,最多减 n 次,\(O(n)\)

831 ???

831. KMP字符串 - AcWing题库

next 在某些头文件里有定义,最好换个变量名;另外KMP算法还可以进一步优化,以下是优化后的算法

#include <algorithm>
#include <cstdio>
#include <iostream>

using namespace std;

const int N = 1e5 + 10;
const int M = 1e6 + 10;
int next_val[N];

int main() {
    int n, m;
    char p[N], s[M];
    cin >> n >> p + 1 >> m >> s + 1;
    // ^ 生成 next_val 数组
    for (int i = 2, j = 0; i <= n; i++) {
        while (j && p[i] != p[j + 1]) j = next_val[j];
        if (p[i] == p[j + 1]) j++;
        // ^ 继续优化,选择回退的最佳位置
        if (j && p[i + 1] == p[j + 1]) {
            next_val[i] = next_val[j];
        } else
            next_val[i] = j;
    }
    // for (int i = 1; i <= n; i++) {
    //     cout << next_val[i] << endl;
    // }
    // ^ KMP 匹配过程
    for (int i = 1, j = 0; i <= m; i++) {
        while (j && p[j + 1] != s[i]) j = next_val[j];
        if (p[j + 1] == s[i]) j++;
        if (j == n) {
            cout << i - n << " ";
            j = next_val[j];
        }
    }
    return 0;
}

字典树 Trie

用于高效存储和查找字符串集合的数据结构

835

835. Trie字符串统计 - AcWing题库

son[N][26] 存的是每个节点所有的儿子是什么,cnt[N] 存的是单词的数量,idx与数组模拟单链表里的idx相同

#include <algorithm>
#include <cstdio>
#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int son[N][26], cnt[N], idx;  // ^ 下标是0的点,既是根节点又是空节点

void insert(char str[]) {
    int p = 0;
    for (int i = 0; str[i]; i++) {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];
    }
    cnt[p]++;
}

int query(char str[]) {
    int p = 0;
    for (int i = 0; str[i]; i++) {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main() {
    cin.tie(0);
    int n;
    cin >> n;
    while (n--) {
        char a, b[N];
        cin >> a >> b;
        if (a == 'I')
            insert(b);
        else
            cout << query(b) << endl;
    }
    return 0;
}

143 ??

143. 最大异或对 - AcWing题库

朴素算法是两层循环并满足 j<i(避免重复, \(C^2_n = n*(n-1)/2\) );时间复杂度 \(O(n^2)\) ,题目给的 n 是 1e5,则 1e10 超时

可以使用 trie tree 优化,每插入一个元素 x,在字典树中查找满足与该元素异或最大值的元素(尽可能反着取,每次查找只要遍历31位),时间复杂度 \(O(n)\)

可以先插入再遍历(少个判断),第一个插入的元素与自身异或为 0

每个节点个数长31,最多1e5个,那么idx最大可以到 31 * 1e5

#include <algorithm>
#include <cstdio>
#include <iostream>

using namespace std;

const int N = 1e5 + 10, M = 31 * N;

int son[M][2], idx;

void insert(int x) {
    int p = 0;
    for (int i = 30; i >= 0; i--) {
        int bit = x >> i & 1;
        if (!son[p][bit]) son[p][bit] = ++idx;
        p = son[p][bit];
    }
}

int query(int x) {
    int res = 0, p = 0;
    fo
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 3/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇乘法逆元及其三种求法 下一篇1.15 自实现GetProcAddress

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目