ACM算法模板–2020总结
写在前面:
几个月来学习了很多有趣的算法,但由于最近课业忙碌,无暇再顾及新的算法。打开笔记本,发现里面的算法笔记东零西落,于是决定整理一番,虽然内容不多,基本上是较为简单的算法,自己才疏学浅,望各位大佬不吝赐教。 (持续更新中)
目录:
1:基础算法
java大数
差分
快速幂,快速乘
小技巧
2:图论
dijkstra
tarjan强连通分量
tarjan求割点(割边)
二分图匹配
Kruskal最小生成树
3:数据结构
二叉搜索树 BST
堆的基本操作
哈夫曼编码
平衡树 AVL
二叉树遍历
线段树
树状数组
4:字符串算法
Manacher
trie字典树
最大异或对
AC自动机
KMP
(一):基础算法
1: java大数
import java.math.BigInteger;
public static void main(String args[]){
Scanner in = new Scanner(System.in);
while(in.hasNext()) //等同于!=EOF
{
BigInteger a;
a = in.nextBigInteger();
}
}
int p = 2;
System.out.print(a.toString(p)); // 输出a的二进制
//基本操作
a.add(b);
a.subtract(b);
a.multiply(b);
a.divide(b);
BigInteger c = b.remainder(a) //取余
a.gcd(b);
a.abs();
a.negate()
a.pow(b);
2:差分
定义:原数组:s[i] ,差分数组 d[i]
1:d[i] = s[i] - s[i-1]
2: s[i] = ∑ i = 1 n d [ i ] \sum_{i=1}^n \ d[i] i=1∑n d[i]
3: 在 区间[ a, b ]内加上数c:
d[a]+c, d[b+1]-c
3: 快速幂,快速乘
ll quick_power(ll a, ll b, ll mods){
ll res = 1;
while(b){
if(b & 1) res = res*a%mods;
a = a*a%mods;
b >>= 1;
}
return res;
}
ll quick_multi(ll a, ll b, ll mods){
ll ans = 0;
while(b){
if(b & 1) ans = (ans+a)%mods;
a = (a+a)%mods;
b >>= 1;
}
return ans;
}
4:小技巧之一:查询二进制中1的个数
for(int i=31;i>=0;i--){
if(m>>i&1 == 1)
ans++;
}
5:小技巧之二: 二路归并法
void merge_sort(int s[]){ //0~n-1
for(int step=2; step/2<n; step*=2){
for(int i=0;i<n; i+=step ){
int mid = i+step/2-1;
if(mid+1<n)
sort(s+i, s+min(i+step-1,n));
}
}
}
6:小技巧之三:欧拉筛求素数
void init(){
for(int i=2;i<=maxn;i++){
if(!vis[i])
prim[cnt++] = i;
for(int j=0;j<cnt;j++){
if(i*prim[j] > maxn) break;
vis[i*prim[j]] = 1;
if(i%prim[j]==0) break;
}
}
}
(二):图论
1:堆优化 dijkstra
#include <bits/stdc++.h>
#define INF 0x3f3f3f
using namespace std;
int n,m,s0,d0,cnt,sum1,sum;
int s[510],s1[510];
struct edge{
int to,cost;
edge(int t,int c):to(t),cost(c){}
bool operator < (const edge &a) const {
return this->cost > a.cost;
}
};
vector<edge> g[510];
vector<int> pre[510];
int d[510],vis[510];
vector<int> tmp_path,path;
void dijkstra(){
priority_queue<edge> q;
memset(d,INF,sizeof(d));
memset(vis,0,sizeof(vis));
d[s0] = 0;
q.push(edge(s0,0));
while(!q.empty()){
edge tmp = q.top();
q.pop();
int x = tmp.to;
if(vis[x]==1) continue;
vis[x] = 1;
for(int i=0;i<g[x].size();i++){
int v = g[x][i].to;
int cost = g[x][i].cost;
if(vis[v]==0 && d[v] > d[x]+cost){
d[v] = d[x]+cost;
q.push(edge(v,d[v]));
pre[v].clear();
pre[v].push_back(x);
}
else if(d[v]==d[x]+cost ){
pre[v].push_back(x);
}
}
}
}
void dfs(int x){
if(x == s0){
cnt++;
if(sum1 < sum){
sum1 = sum;
path = tmp_path;
}
return;
}
for(int i=0;i<pre[x].size();i++){
int v = pre[x][i];
if(vis[v]==0){
vis[v] = 1;
sum += s[v];
tmp_path.push_back(v);
dfs(v);
tmp_path.pop_back();
vis[v] = 0;
sum -= s[v];
}
}
}
int main(){
cin>>n>>m>>s0>>d0;
for(int i=0;i<n;i++){
cin>>s[i];
}
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
g[a].push_back(edge(b,c));
g[b].push_back(edge(a,c));
}
dijkstra();
memset(vis,0,sizeof(vis));
dfs(d0);
cout<<cnt<<" "<<sum1+s[d0]<<endl;
for(int i=path.size()-1;i>=0;i--){
cout<<path[i]<<" ";
}
cout<<d0<<endl;
return 0;
}
2:tarjan求强连通分量
/* poj 1236:A:求至少给几个学校发送文件,使所有学校都能接收到
B: 有向有环图再增几条边成为强连通图
tarjan:求强连通分量的个数
1:缩点:将强连通分量>1的变成一个点,再将原连接其它点的边连上
2: A:求缩点后的入度为 0 的点的个数
3;B:先缩点成DAG,edge = max(入度为 0个数,出度为 0个数)
因为:从DAG变为强连通图 :将出度为 0的点连到入度为 0 的点即可
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <cstring>
#define maxn 1000
using namespace std;
int dfn[maxn+2],low[maxn+2];
int n,num,counts,belong[maxn+2];
stack<int> s;
vector<int> g[maxn+2];
int instack[maxn+2];
int income[maxn+2]; //每个强连通分量的入度与出度
int outcome[maxn+2];
void tarjan(int u){
dfn[u] = low[u] = ++counts;
s.push(u);
instack[u] = 1;
for(int i=0;i<g[u].size();i++){
int v = g[u][i];
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(instack[v] == 1){
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]){ //节点 u是强连通分量的根
num++;
while(true){
int j = s.top();
s.pop();
instack[j] = 0;
belong[j] = num;
if(j == u)
break;
}
}
}
void print(){
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int a;
while(~scanf("%d",&a) && a){
g[i].push_back(a);
}
}
//求强连通分量的个数
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i);
}
}
//缩点成 DAG 强连通分量个数为 num,出度入度均为每个强连通分量(缩成点)
for(int i=1;i<=n;i++){
for(int j=0;j<g[i].size();j++){
int u = i;
int v = g[i][j];
if(belong[u] != belong[v]){
outcome[belong[u]]++;
income[belong[v]]++;
}
}
}
int t=0,t1=0;
for(int i=1;i<=num;i++){
if(income[i]==0) t++;
if(outcome[i]==0) t1++;
}
cout<<t<<endl;
//特判:原图为一个强连通分量
if(num==1) cout<<"0"<<endl;
else cout<<max(t1,t)<<endl;
return 0;
}
3: tarjan 求割点(割边)
#include <bits/stdc++.h>
#define maxn 210000
using namespace std;
int n,m,counts,num,k; // dfn[]:dfs第一次到该点的时间
int dfn[maxn+2],low[maxn+2]; // low[]:能返回到的最小的时间戳
int cut[maxn+2],s[maxn+2];
vector<int> g[maxn+2];
void tarjan(int u, int pre){
dfn[u] = low[u] = ++counts;
int child = 0;
for(int i=0;i<g[u].size();i++){
int v = g[u][i];
if(dfn[v]==0){
child++;
tarjan(v,u);
low[u] = min(low[u],low[v]); //看 u是否能通过子树回到更早的节点
if(low[v] >= dfn[u] && pre!=u) //看 u的前辈与 v及其子树相连通
cut[u] = 1;
}
else low[u] = min(low[u], dfn[v]);
}
if(u==pre && child >= 2)
cut[u] = 1;
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0)
tarjan(i,i);
}
int k=0;
for(int i=1;i<=n;i++){
if(cut[i]==1){
num++;
s[k++] = i;
}
}
cout<<num<<endl;
for(int i=0;i<k;i++){
if(i!=k-1) cout<<s[i]<<" ";
else cout<<s[i]<<endl;
}
return 0;
}
4:二分图匹配
//邻接表做法
#include <bits/stdc++.h>
#define maxn 550
using namespace std;
int n,m,e;
int used[maxn],s[maxn];
vector<int> g[maxn];
bool check(int a,int b){
for(int i=0;i<g[a].size();i++){
if(g[a][i] == b) return false;
}
return true;
}
bool find(int i){
for(int j=0;j<g[i].size();j++){
int x = g[i][j];
if(used[x]==0){
used[x] = 1;
if(s[x]==0 || find(s[x])){
s[x] = i;
return true;
}
}
}
return false;
}
int main(){
cin>>n>>m>>e;
while(e--){
int a,b;
cin>>a>>b;
if(check(a,b))
g[a].push_back(b);
}
int sum = 0;
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
if(find(i)) sum++;
}
cout<<sum<<endl;
}
5: Kruskal
//求最小生成树
#include <bits/stdc++.h>
using namespace std;
int n,pre[10010];
struct edge{
int from,to,cost;
}s[10010];
bool cmp(struct edge a,struct edge b){
if(a.cost<b.cost) return true;
return false;
}
void initial(int *pre){
for(int i=0;i<n;i++)
pre[i] = i;
}
int find(int x){
if(pre[x]==x) return pre[x];
return pre[x] = find(pre[x]);
}
int kruskal(){
int res = 0,x,y;
sort(s,s+n,cmp); // (1) 按边从小到大排序
initial(pre); // (2) 初始化并查集
for(int i=0;i<n;i++){
x=find(s[i].from);
y=find(s[i].to);
if(x!=y) // (3) 将边两头顶点合并
{
pre[x] = y;
res += s[i].cost;
}
}
return res;
}
int main(){
int m,res=0;
cin>>n>>m; //点的数量,边的个数
for(int i=0;i<m;i++)
cin>>s[i].from>>s[i].to>>s[i].cost;
res = kruskal(); //最小生成树路径最小值
return 0;
}
(三) 数据结构
1:二叉搜索树 BST
/*
二叉查找树 (二叉搜索树)
特点:左子树小于根,右子树大于根
*/
#include <bits/stdc++.h>
using namespace std;
struct node{
int data;
struct node* left;
struct node* right;
};
void find(struct node* root, int x){ //查找
if(root==NULL){
cout<<"No find"<<endl;return;
}
if(x == root->data) cout<<"find it!"<<endl;
else if(x < root->data) find(root->left, x);
else find(root->right, x);
}
void insert(struct node* &root, int x){ //插入 (引用才能修改结点
if(root==NULL){
// root->data = x; 空指针没有数据,这样写法错误!! (要创建新节点
struct node* p;
p = (struct node*)malloc(sizeof(struct node));
p->data = x;p->left = p->right = NULL;
root = p;
return;
}
if(root->data == x) //结点已存在
return;
else if(root->data < x)
insert(root->right, x);
else insert(root->left, x);
}
struct node* buildtree(int s[],int n){ //将数组建立成二叉搜索树
node *root = NULL;
for(int i=0;i<n;i++){
insert(root, s[i]);
}
return root;
}
struct node* findmax(struct node* root){
while(root->right)
root = root->right;
return root;
}
struct node* findmin(struct node* root){
while(root->left)
root = root->left;
return root;
}
void deleted(struct node* &root, int x){ //删除
if(root == NULL) return; //先查找后删除
if(root->data == x){//分类讨论
if(root->left == NULL && root->right == NULL) //若是叶子结点,直接删
root = NULL;
else if(root->left){ //左子节点不空,找前驱搬上来
struct node* pre = findmax(root->left);
root->data = pre->data;
deleted(root->left, pre->data);
}
else { //等价左子树 ,找后继搬上来
struct node* next = findmin(root->right);
root->data = next->data;
deleted(root->right, next->data);
}
}
else if(x < root->data)
deleted(root->left, x);
else deleted(root->right, x);
}
//前序遍历
void pre_order(struct node* root){
if(root == NULL) return;
printf("%d ",root->data);
pre_order(root->left);
pre_order(root->right);
}
int main(){
int s[10010];
string a;
struct node* L;
cin>>a;
for(int i=0;i<a.size();i++)
s[i] = a[i]-'0';
L = buildtree(s,a.size()); //用层序遍历数组读入建树
pre_order(L);
cout<<endl;
find(L,4);
cout<<endl;
insert(L,4);
cout<<endl;
deleted(L,5);
cout<<endl;
pre_order(L);
}
2:堆的基本操作
//堆的基本操作
#include <bits/stdc++.h>
using namespace std;
struct maxheap{ //大顶堆
int *s; //存放数组元素
int size; //当前容量
int capicity; //堆的最大容量
};
struct maxheap* create(int maxsize){ //创建堆 最大容量 maxsize
maxheap *h = new maxheap;
h->s = new int[maxsize+1];
h->size = 0;
h->capicity = maxsize;
h->s[0] = 0x3f3f3f3f; //哨兵,堆从 1 往后排
}
void insert(struct maxheap* h, int x){ //堆的插入
if(h->size >= h->capicity){
printf("The heap is full\n");
return;
}
int i = ++h->size; //指向最后的位置
int j = i/2; //父节点
for( ; h->s[j]<x; i=j){ //当父节点小于x
h->s[i] = h->s[j]; //子节点与父节点交换
}
h->s[i] = x; //插入合适的位置
}
int deleted(struct maxheap* h){ //返回堆顶元素
if(h->size == 0){
printf("Empty!\n");
return 0;
}
int i,j;
int maxn,temp;
maxn = h->s[1]; //堆顶
temp = h->s[h->size--]; //最后一个
for(i=1; i*2<=h->size; i=j){
j = i*2; // i 父节点
if(j+1<=h->size && h->s[j+1] > h->s[j])
j = j+1; // j 指向左右儿子较大者
if(temp >= h->s[j])
break;
else
h->s[i] = h->s[j];
}
h->s[i] = temp;
return maxn;
}
3:哈夫曼编码
#include <bits/stdc++.h>
using namespace std;
int heap[10010];
int size = 0;
void insert(int x){ //插入最小堆
heap[++size] = x;
//向上调整
int i = size;
int j = i/2;
for( ; heap[j]>x; i=j,j=i/2)
heap[i] = heap[j];
heap[i] = x;
}
int deleted(){
int x = heap[1];
int tmp = heap[size];
heap[size--] = 0;
//向下调整
int i = 2;
for( ;i<=size;i *= 2){
if(i+1<=size && heap[i+1] < heap[i])
i++;
if(heap[i] < tmp)
heap[i/2] = heap[i];
else break;
}
heap[i/2] = tmp;
return x;
}
bool has_prefix(char s[][1000], int n){ //判断前缀码
int t[10010],mt[10010];
for(int i=0;i<10010;i++) t[i]=1;
memset(mt,0,sizeof(mt));
for(int i=0;i<n;i++){
for(int j=0;j<strlen(s[i]);j++){
if(s[i][j] == '0')
t[i] = t[i]*2;
else t[i] = t[i]*2+1;
}
mt[t[i]]++;
}
int flag=0;
for(int i=0;i<n;i++){
int j = t[i];
if(mt[j] != 1) return false;
while(j){
j /= 2;
if(mt[j]==1){
flag = 1;
break;
}
}
if(flag==1) return false;
}
return true;
}
int main(){
map<char,int> m;
int n;
heap[0] = -1;
cin>>n;
for(int i=0;i<n;i++){
char c;
int q;
cin>>c>>q;
m[c] = q;
insert(q);
}
int size0 = size; //size有变化
int wpl = 0;
for(int i=0;i<size0-1;i++){ //delete size-1次,每次取出两个,放入一个
int p,q;
p = deleted();
q = deleted();
insert(p+q);
wpl += p+q; //数的带权路径长度 = 所有叶子节点的带权路径之和
// = 各分支结点之和
}
int d;
cin>>d;
while(d--){
int w1=0;
char c;
char s[100][1000];
for(int i=0;i<n;i++){
cin>>c>>s[i];
int q = strlen(s[i]);
w1 += q * m[c];
}
if(w1 == wpl && has_prefix(s,n)){
cout<<"Yes"<<endl;
}
else cout<<"No"<<endl;
}
return 0;
}
4:平衡树 AVL树
#include <bits/stdc++.h>
using namespace std;
struct node{
int data;
int height;
struct node * left;
struct node * right;
};
int get_height(struct node * root){ //高度
if(root == NULL) return 0; //特判,必不可少
return root->height;
}
void update_height(struct node * root){ //更新结点高度, 取大值+1
root->height = max(get_height(root->left), get_height(root->right)) + 1;
}
void turn_left(struct node* &root){ //左旋
struct node * tmp;
tmp = new node;
tmp = root->right;
root->right = tmp->left;
tmp->left = root;
update_height(root);
update_height(tmp); //记得更新
root = tmp;
}
void turn_right(struct node * &root){ //右旋
struct node * tmp;
tmp = new node;
tmp = root->left;
root->left = tmp->right;
tmp->right = root; // 失误点
update_height(root);
update_height(tmp);
root = tmp;
}
int getfactor(struct node * root){ //平衡因子:左减右
return get_height(root->left) - get_height(root->right);
}
void insert(struct node* &root, int x){
if(root==NULL){
struct node* p;
p = new node;
p->data = x;
p->height = 1;
p->left = NULL;
p->right = NULL;
root = p; //别忘了
}//cout<<x<<" "<<root->data<<endl;
if(x < root->data){
insert(root->left, x); //深搜返回后更新
update_height(root);
int factor = getfactor(root); //根的平衡系数
if(factor == 2){
int factor_left = getfactor(root->left);
if(factor_left == -1){
turn_left(root->left);
turn_right(root);
}
else if(factor_left == 1){
turn_right(root);
}
}
}
else if(x > root->data){
insert(root->right, x); //深搜返回后更新
update_height(root);
int factor = getfactor(root); //根的平衡系数
if(factor == -2){
int factor_right = getfactor(root->right);
if(factor_right == -1){
turn_left(root);
}
else if(factor_right == 1){
turn_right(root->right);
turn_left(root);
}
}
}
}
int main(){
int n,a;
struct node * root;
root = (struct node*)malloc(sizeof(node));
root = NULL;
cin>>n;
while(n--){
cin>>a;
insert(root, a);
}
cout<<root->data<<endl;
return 0;
}
5:二叉树的遍历
//中序 + 后序建树
struct node* build(int midL, int midR, int proL, int proR){
if(midL > midR || proL > proR){
return NULL;
}
struct node *root;
root = new node;
root->data = pro[proR];
root->lchild = root->rchild = NULL;
int k,mark;
for(k=midL; k<=midR;k++){
if(mid[k] == pro[proR]) break;
}
mark = k - midL;
root->lchild = build(midL, midL+mark-1, proL, proL+mark-1);
root->rchild = build(midL+mark+1, midR, proL+mark, proR-1);
return root;
}
6:线段树
struct node{
int l,r,weight,mark; //左孩子,右孩子,区间和,延时标记
}tree[maxn*4+10]; //开四倍空间
void build(int l, int r, int root){
tree[root].l = l;
tree[root].r = r;
if(l == r){
scanf("%d", &tree[root].w); //叶子节点的值
return;
}
int mid = l+(r-l)/2;
build( l ,mid, root*2);
build(mid+1,r, root*2+1);
tree[root].w = tree[root*2].w + tree[root*2+1].w; //类似归并
}
//向下修改
void push_down(int root){
if(tree[root].mark){
int mark = tree[root].mark;
tree[root*2].mark += mark;
tree[root*2+1].mark += mark;
tree[root*2].w += mark * (tree[root*2].r-tree[root*2].l+1);
tree[root*2+1].w += mark * (tree[root*2+1].r-tree[root*2+1].l+1);
tree[root].mark = 0;
}
}
//区间修改
void modify_section(int root, int l, int r, int k){
if(tree[root].l >= l && tree[root].r <= r){
tree[root].w += k*(tree[root].r-tree[root].l+1); //值 = 子个数 * k
tree[root].mark += k;
return;
}
if(tree[root].mark)
push_down(root);
int mid = (tree[root].l + tree[root].r)/2; //mid是root的中间
if(l<=mid) modify_section(root*2, l, r, k); //注意:l,r不变
if(mid<r) modify_section(root*2+1, l, r, k);
tree[root].w = tree[root*2].w + tree[root*2+1].w;
}
//单点修改
void change_point(int root,int x, int k){
if(tree[root].l == tree[root].r){
tree[root].w += k;
return ;
}
if(tree[root].mark) push_down(root);
int mid = (tree[root].l + tree[root].r)/2;
if(x<=mid)
change_point(root*2, x, k);
else
change_point(root*2+1, x, k);
tree[root].w = tree[root*2].w + tree[root*2+1].w;
}
//区间查询
void check_section(int root,int l,int r, int &ans){
if(tree[root].l >= l && tree[root].r <= r){
ans += tree[root].w;
return;
}
if(tree[root].mark)
push_down(root);
int mid = (tree[root].l + tree[root].r)/2;
if(l<=mid) check_section(root*2, l, r, ans);
if(mid<r) check_section(root*2+1, l, r, ans);
}
//单点查询
void check_section(int root,int l,int r, int &ans){
if(tree[root].l >= l && tree[root].r <= r){
ans += tree[root].w;
return;
}
if(tree[root].mark)
push_down(root);
int mid = (tree[root].l + tree[root].r)/2;
if(l<=mid) check_section(root*2, l, r, ans);
if(mid<r) check_section(root*2+1, l, r, ans);
}
7:树状数组
/* 树状数组 : lowbit 思想:
lowbit(x): 只留下x二进制最右边的 1,其它 1变为 0
可完成:单点修改,区间求和
无法完成:复杂度较小的区间修改 */
#include <bits/stdc++.h>
#define maxn 50050
#define lowbit(x) ((x)&(-x))
using namespace std;
int t,n,s,ans;
int c[maxn];
/* 单点修改:将下标为 x的位置加上 val
将所有包含 x的区间树都加上 val */
void update(int x, int val){
for(int i=x;i<=n;i+=lowbit(i))
c[i] += val;
}
/* 区间求和:从下标为 1到 x的前缀和 */
int getsum(int x){
int sum=0;
for(int i=x;i>=1;i-=lowbit(i)){
sum += c[i];
}
return sum;
}
(四)字符串算法
1:Manacher
char s[maxn],s1[maxn*2+10];
int p[maxn*2+10]; //注意p是s1串的回文项
int init(){
int j = 0;
for(int i=0;i<n;i++){
s1[j++] = '#';
s1[j++] = s[i];
}
s1[j++] = '#';
return j;
}
//首先:c :回文中心 r:回文右端点
//(1)当且仅当 i <r 并且 i’ 的回文半径不超过L 时,有 p[i] == p[i’],而 i’ 实际 = C*2-i;
//(2) 当且仅当 i<r 并且 i‘ 的回文半径超过 L时, p[i] = R-i;
//(3)当且仅当 i<r 并且p[i’]的半径刚好到L:p[i]的半径有可能超出R,需按暴力法继续匹配:
//(4)当 i>=r 的时候,即不在已知回文范围内,p[i] = 1;
void Manacher(){
int n = init();
int c = -1,r = -1,maxn = -1;
for(int i=0;i<n;i++){
if(i>=r)
p[i] = 1;
else
p[i] = min(p[c*2-i], r-i); //情况 1,2
while(p[i]+i < n && i-p[i]>=0){ //情况 3
if(s1[p[i]+i] == s1[i-p[i]])
p[i]++;
else break;
}
if(p[i]+i > r){ //更新
r = p[i]+i;
c = i;
}
maxn = max(maxn, p[i]);
}
maxn = maxn-1; //重点,最后一定要减一
cout<<maxn<<endl;
}
2: trie字典树
#include <iostream>
#define maxn 110000
using namespace std;
int trie[maxn][26];
int n,k,cnt[maxn];
void insert(string s){
int m = 0;
for(int i=0;i<s.size();i++){
int p = s[i]-'a';
if(!trie[m][p]) trie[m][p] = ++k;
m = trie[m][p];
}
cnt[m]++;
}
int query(string s){
int m = 0;
for(int i=0;i<s.size();i++){
int p = s[i]-'a';
if(trie[m][p]==0) return 0;
m = trie[m][p];
}
return cnt[m];
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
string s,s1;
cin>>s>>s1;
if(s[0]=='I') insert(s1);
else cout<<query(s1)<<endl;
}
return 0;
}
3:最大异或对
#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int trie[maxn*31][2];
int n,t,cnt,res = -1;
void insert(int x){
int p = 0;
for(int i=30; i>=0; i--){
int k = x>>i&1;
if(!trie[p][k]) trie[p][k] = ++cnt;
p = trie[p][k];
}
}
void solve(int x){
int p = 0;
int ans = 0;
for(int i=30;i>=0;i--){
int k = x>>i&1;
if(trie[p][!k]){
p = trie[p][!k];
ans = ans*2+1;
}
else{
p = trie[p][k];
ans = ans*2;
}
}
res = max(ans, res);
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>t;
insert(t);
solve(t);
}
cout<<res<<endl;
return 0;
}
4:AC自动机
#include <bits/stdc++.h>
#define maxn 2100000
using namespace std;
int trie[maxn][26];
int word[maxn];
int fail[maxn];
int cnt,ans;
void insert(string s){
int root = 0;
for(int i=0;i<s.size();i++){
int p = s[i]-'a';
if(!trie[root][p]) trie[root][p] = ++cnt;
root = trie[root][p];
}
word[root]++;
}
void getfail(){
queue<int> q;
for(int i=0;i<26;i++){
int m = trie[0][i];
if(m!=0){
fail[m] = 0;
q.push(m);
}
}
while(!q.empty()){
int now = q.front();
q.pop();
for(int i=0;i<26;i++){
int m = trie[now][i]; //m = now的子节点
if(m!=0){
fail[m] = trie[fail[now]][i];
q.push(m);
}
else
trie[now][i] = trie[fail[now]][i];
}
}
}
void query(string s){
int root = 0;
for(int i=0;i<s.size();i++){
root = trie[root][s[i]-'a'];
for(int j=root; j&&word[j]!=-1 ; j=fail[j]){
ans += word[j];
word[j] = -1;
}
}
}
5: KMP
//标准KMP
#include <bits/stdc++.h>
using namespace std;
int n,l1,l2;
int s[1001000],s1[1001000],nexts[1001000];
void getnext(int ss[], int len){
int j=-1;
nexts[0] = -1;
for(int i=1;i<len;i++){
while(j!=-1 && ss[i] != ss[j+1])
j = nexts[j];
if(ss[i] == ss[j+1])
j++;
nexts[i] = j;
}
}
int main(){
scanf("%d",&n);
while(n--){
scanf("%d%d",&l1,&l2);
memset(s,0,sizeof(s));
memset(s1,0,sizeof(s1));
memset(nexts,0,sizeof(nexts));
for(int i=0;i<l1;i++) scanf("%d",&s[i]);
for(int i=0;i<l2;i++) scanf("%d",&s1[i]);
getnext(s1,l2);
int j = -1;
int flag1 = 0;
for(int i=0;i<l1;i++){
while(j!=-1 && s[i]!=s1[j+1]){
j = nexts[j];
}
if(s[i] == s1[j+1])
j++;
if(j==l2-1){
/*
第一种
cout<<i-j+1<<endl;
flag1 = 1;
break;
第二种:求子串个数
num++;
j = nexts[j];
*/
}
}
if(flag1==0) cout<<"-1"<<endl;
}
return 0;
}