ACM算法模板--2020总结

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=1n 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;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ACM 算法模板集 Contents 一. 常用函数与STL 二. 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 三. 大数模板,字符读入 四. 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 五. 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 六. 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 七. 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值