算法模板(Java版)_字符串、并查集和堆

@ZZHow(ZZHow1024)

字符串

KMP字符串

| 💡

致敬三位前辈:D.E.Knuth、J.H.Morris 和 V.R.Pratt!

字符串模式匹配算法,大大避免重复遍历的情况,克努特-莫里斯-普拉特算法。

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static final int N = 100010;
    static final int M = 1000010;
    static char[] p = new char[N];
    static char[] s = new char[M];
    static int[] ne = new int[N];
    
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
        int n = Integer.parseInt(br.readLine());
        String pStr = " " + br.readLine();
        int m = Integer.parseInt(br.readLine());
        String sStr = " " + br.readLine();
        
        p = pStr.toCharArray();
        s = sStr.toCharArray();
        
        // 计算 next 数组
        for (int i = 2, j = 0; i <= n; i++) {
            while (j != 0 && p[i] != p[j + 1])
                j = ne[j];
            if (p[i] == p[j + 1])
                j++;
            ne[i] = j;
        }
        
        // KMP 匹配
        for (int i = 1, j = 0; i <= m; i++) {
            while (j != 0 && s[i] != p[j + 1])
                j = ne[j];
            if (s[i] == p[j + 1])
                j++;
            if (j == n) {
                bw.write((i - n) + " ");
                j = ne[j];
            }
        }
        
        br.close();
        bw.close();
    }
}

Trie树

| 💡

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

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static final int N = 100010;
    static int[][] son = new int[N][26]; // 存储所有节点的儿子是什么
    static int[] cnt = new int[N]; // 字符串结束标记且进行计数
    static int idx; // 下标为 0 的点,既是跟节点,又是空节点
    
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        
        int n = Integer.parseInt(br.readLine());
        while (n-- != 0) {
            String[] lines = br.readLine().split(" ");
            char op = lines[0].charAt(0);
            String str = lines[1];
            switch (op) {
                case 'I': {
                    insert(str.toCharArray());
                    break;
                }
                case 'Q': {
                    bw.write(query(str.toCharArray()) + "");
                    bw.newLine();
                }
            }
        }
        
        br.close();
        bw.close();
    }
    
    // 插入字符串
    public static void insert(char[] str) {
        int p = 0;
        for (int i = 0; i < str.length; i++) {
            int u = str[i] - 'a';
            if (son[p][u] == 0)
                son[p][u] = ++idx;
            p = son[p][u];
        }
        
        cnt[p]++;
    }
    
    // 查询字符串出现的次数
    public static int query(char[] str) {
        int p = 0;
        for (int i = 0; i < str.length; i++) {
            int u = str[i] - 'a';
            if (son[p][u] == 0)
                return 0;
            p = son[p][u];
        }
        
        return cnt[p];
    }
}

并查集

| 💡

两个操作:

  1. 将两个集合合并。
  2. 询问两个元素是否在一个集合当中。

基本原理:每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点存储

它的父节点,Px 表示 x 的父节点。

问题 1:如何判断树根 if (p[x] == ×) {return true;}

问题 2:如何求 x 的集合编号 while (p[x] != x) x = p[x];

问题 3:如何合并两个集合,px 是 x 的集合编号,py 是 y 的集合编号 p[x] = y;

java 复制代码
import java.util.Scanner;

public class Main {
    static int[] p;
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int n = scanner.nextInt();
        p = new int[n + 10];
        for (int i = 1; i <= n; i++)
            p[i] = i;
        int m = scanner.nextInt();
        
        while (m-- != 0) {
            char op = scanner.next().charAt(0);
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            
            switch (op) {
                case 'M': {
                    p[find(a)] = find(b);
                    break;
                }
                case 'Q': {
                    System.out.println((find(a) == find(b)) ? "Yes" : "No");
                    break;
                }
            }
        }
    }
    
    // 返回祖宗节点 + 路径压缩
    public static int find(int x) {
        if (x != p[x])
            p[x] = find(p[x]);
        
        return p[x];
    }
}

| 💡

手写堆,功能:

  1. 插入一个数:heap[++size] = x; up(size);
  2. 求集合中的最小值:heap[1];
  3. 删除最小值:heap[1] = heap[size]; size--; down(1);
  4. 删除任意一个元素:heap[1] = heap[k]; size--; down(1); up(1);
  5. 修改任意一个元素:heap[k] = x; down(k); up(k);

使用一维数组存储:

  • x 左儿子:2x
  • x 右儿子:2x + 1
java 复制代码
import java.util.Scanner;

public class Main {
    static final int N = 100010;
    static int[] h;
    static int[] ph;
    static int[] hp;
    static int size;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();
        h = new int[n + 10];
        ph = new int[n + 10];
        hp = new int[n + 10];
        int m = 0;

        while (n-- != 0) {
            String op = scanner.next();

            switch (op) {
                case "I": {
                    int x = scanner.nextInt();

                    size++;
                    m++;
                    ph[m] = size;
                    hp[size] = m;
                    h[size] = x;
                    up(size);
                    break;
                }
                case "PM": {
                    System.out.println(h[1]);
                    break;
                }
                case "DM": {
                    heapSwap(1, size);
                    size--;
                    down(1);
                    break;
                }
                case "D": {
                    int k = scanner.nextInt();

                    k = ph[k];
                    heapSwap(k, size);
                    size--;
                    down(k);
                    up(k);
                    break;
                }
                case "C": {
                    int k = scanner.nextInt();
                    int x = scanner.nextInt();

                    k = ph[k];
                    h[k] = x;
                    down(k);
                    up(k);
                    break;
                }
            }
        }
    }

    public static void heapSwap(int a, int b) {
        int temp = ph[hp[a]];
        ph[hp[a]] = ph[hp[b]];
        ph[hp[b]] = temp;

        temp = hp[a];
        hp[a] = hp[b];
        hp[b] = temp;

        temp = h[a];
        h[a] = h[b];
        h[b] = temp;
    }

    public static void up(int u) {
        while (u / 2 != 0 && h[u / 2] > h[u]) {
            heapSwap(u / 2, u);
            u /= 2;
        }
    }

    public static void down(int u) {
        int t = u; // t 表示三个节点中最小节点的编号
        if (u * 2 <= size && h[u * 2] < h[t])
            t = u * 2;
        if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t])
            t = u * 2 + 1;
        if (u != t) {
            heapSwap(u, t);
            down(t);
        }
    }
}
相关推荐
TDengine (老段)几秒前
TDengine 免费版说明
java·大数据·数据库·物联网·时序数据库·tdengine
码上有光4 分钟前
异常和智能指针
java·大数据·c++·servlet·异常·智能指针
变量未定义~7 分钟前
最小生成树1(Prim模板)、最小生成树2(kruskal模板)、最近公共祖先(模板)
算法
这就是佬们吗10 分钟前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
LONGZETECH18 分钟前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车
自学小白菜20 分钟前
关于提升机器学习算法做预测回归任务精度的方法分享
算法·机器学习·回归·提升精度
学废了wuwu28 分钟前
从入门到实战:深入浅出模型压缩三大核心——剪枝、彩票假设与知识蒸馏
人工智能·算法·剪枝
小肝一下39 分钟前
3. 单链表
c语言·数据结构·c++·算法·leetcode·链表·dijkstra
劈星斩月1 小时前
监督学习算法 之 决策树
学习·算法·决策树
学计算机的计算基1 小时前
操作系统内存管理全解:虚拟内存、页表、COW、malloc、OOM一篇搞定
java·笔记·算法