基础算法

根据数据范围推算法

image-20210514184551333

常用的STL

1.stack<T> s;	push,pop,empty,size;

2.queue<T> q;	push,pop,front,back,empty,size;

3.priority_queue<T> q;		push,pop,top,empty,size;//默认对头是最大的
自定义参数排序:
struct node{
    int d;
    bool operator< (const node& a)const{
        return d > a.d;	//最小值优先
    }
};

4.map<T,T> mp;	//自动排序:按照第一个参数进行升序
mp[T] = T;
mp.insert(pair<T,T>(T,T));
//遍历
map<T,T>::iterator it;
for(it = mp.begin(); it != mp.end(); it++){
      T x = it->first;
      T y = it->second;
}
unordered_map

5.set<T> s; s.insert(T);	//默认升序排序
//遍历
set<T>::iterator it;	//正向
set<T>::reverse_iterator it;	//反向
for(it = s.begin(); it!=s.end(); it++);
//检索
if(s.find(T)!=s.end())	//检索成功

6.int le = lower_bound(arr,arr+n,vable)-arr;	//查找第一个大于或等于某个元素的位置
  int le = upper_bound(arr,arr+n,vable)-arr;	//查找第一个大于某个元素的位置
  set<int> s; s.lower_bound(vable);s.upper_bound(vable);//返回一个迭代器,可以用*s.lower_bound(vable)显示出来

7.全排列
  next_permutation(a,a+n);	//求下一个排列
  prev_permutation(a,a+n);	//求上一个排列

8.去重
  unique是在algorithm中的一个函数,它作用是去掉相邻的重复元素,不要求元素一定有序
  int size = unique(a,a+n)-a;	//unique返回去相邻重复元素后元素末尾地址,所以计算数组大小要减首地址
  vector去重
   a.erase(unique(a.begin(), a.end()), a.end());
  去除数组全部重复的元素,需要事先排序

9.第k位数
  nth_element(数组名,数组名+第k小元素,数组名+元素个数);printf("%d",a[k]);
 
10.vector string push_back() pop_back()

11.内置排序
sort(vc.begin(),vc.end(),[&](pair<int,int> a,pair<int,int> b){
	return a.first < b.first;
});
12.vector在某个元素前添加一个元素
iterator erase(iterator it):删除向量中迭代器指向元素
erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素
void clear():清空向量中所有元素

高精度

//加法:满足 C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B){
    if (A.size() < B.size()) return add(B, A);
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ ){
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if (t) C.push_back(t);
    return C;
}
//减法:满足 C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B){
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ ){
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}
//高精乘低精:满足 C = A * b, A >= 0, b > 0
vector<int> mul(vector<int> &A, int b){
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ ){
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}
// 高精除低精:满足A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r){
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- ){
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

二分查找

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:(所求目标出现的第一个位置)
int bsearch_1(int l, int r){
    while (l < r){
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:(所求目标出现的最后一个位置)
int bsearch_2(int l, int r){
    while (l < r){
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
//只用于求大于1的浮点数二分。
double bsearch_3(double l, double r){
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求保留的位数+2
    while (r - l > eps){
        double mid = (l + r) / 2;
        if (mid*mid*mid>=x) r = mid;
        else l = mid;
    }
    return l;
}

排序算法

//快速排序
void quick_sort(int q[], int l, int r){
    if (l >= r) return;
    int i = l - 1, j = r + 1, x = q[(l+r)>>1];
    while (i < j){
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
//归并排序
void merge_sort(int q[], int l, int r){
    if (l >= r) return;
    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r){
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];
    }
    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];
    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
//归并排序逆序对
int n, a[500005], tmpA[500005];
long long cnt;
void merge_sort(int l, int r, int *A) {
	if (l >= r) return ;
	int mid = l+(r-l)/2;
	merge_sort(l, mid, A);
	merge_sort(mid + 1, r, A);
	int pl = l, pr = mid + 1, tmpp = 0;
	while(pl <= mid && pr <= r) {
		if (A[pl] <= A[pr]) 
			tmpA[tmpp++] = A[pl++];
		else 
			tmpA[tmpp++] = A[pr++], cnt += mid - pl + 1;//这个是求逆序对的
	}
	while(pl <= mid) 
		tmpA[tmpp++] = A[pl++];
	while(pr <= r) 
		tmpA[tmpp++] = A[pr++];
	for (int i = 0; i < tmpp; i++) 
		A[i + l] = tmpA[i];
}

前缀和与差分

前缀和: 
一维前缀和 s[i] = s[i-1] + a[i]
从x1->x2 的和 s[x2] - s[x1-1] 
二维前缀和 s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1] 
//从(x1,y1)->(x2,y2)的矩阵和 s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]

差分:
一维
void insert(int l, int r, int c){
    b[l] += c;
    b[r + 1] -= c;
}
for(int i = 1; i <= n; i++) insert(i, i, a[i]);
while (m -- ){
    int l, r, c;
    scanf("%d%d%d", &l, &r, &c);
    insert(l, r, c);
}
for(int i = 1; i <= n; i++) b[i] += b[i - 1];
二维
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int c){
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i ++ )
    for(int j = 1; j <= m; j ++ )
        scanf("%d", &a[i][j]);
for(int i = 1; i <= n; i ++ )
    for(int j = 1; j <= m; j ++ )
        insert(i, j, i, j, a[i][j]);
while(q -- ){
    int x1, y1, x2, y2, c;
    cin >> x1 >> y1 >> x2 >> y2 >> c;
    insert(x1, y1, x2, y2, c);
}
for(int i = 1; i <= n; i ++ )
    for(int j = 1; j <= m; j ++ )
        b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
//从(x1,y1)->(x2,y2)的矩阵和 s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]

小顶堆

#include <bits/stdcpp.h>
using namespace std;
int tot = 0;
int heap[10010];
int n;
void insert(int x){	//小顶堆 
	tot++;
	heap[tot] = x;
	int i = tot;
	int j = i/2;
	while((j > 0) && heap[j] > heap[i]){
		swap(heap[j],heap[i]);
		i = i / 2;	//i = j / 2;
		j = i / 2;
	}
}
int pop(){
	int x = heap[1];
	heap[1] = heap[tot];
	tot--;
	int i = 1;
	int j = i * 2;
	if(j + 1 <= tot && heap[j+1] < heap[j])j++;
	while(j <= tot && heap[j] < heap[i]){
		swap(heap[j],heap[i]);
		i = j;
		j = i * 2;
		if(j + 1 <= tot && heap[j+1] < heap[j]) j++;
	}
	return x;
}
int main(){
	cin >> n;
	for(int i = 0; i < n; i++){
		int x;
		cin >> x;
		insert(x);
	}
	int ans = 0;
	while(tot >= 2){
		int a = pop();
		int b = pop();
		ans += a + b;
		insert(a + b); 
	}
	cout << ans;
	return 0;
} 

求日期

const char *name[]={"monday","tuesday","wednesday","thursday","friday","saturday","sunday"};
int CaculateWeekDay(int y,int m,int d){
    int a;
     // 1月2月当作前一年的13,14月
    if(m==1||m==2){
        m += 12;
        y--;
     }
     //判断是否在1752年9月3号之前
     if(y<1752||(y==1752&&m<9)||(y==1752&&m==9&&d<3)){
        a =  (d+2*m+3*(m+1)/5+y+y/4+5)%7;                                       
     } 
     else {
          a = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;
     } 
     return a;                                                    
}

约瑟夫环问题

#include <bits/stdcpp.h>
using namespace std;
int main(){
	int n,m;
  	while(cin>>n>>m){
  		if(!n&&!m)break;
		vector<int>q(n);
		for(int i = 0 ; i < n; i++)
	    	q[i] = i + 1;
		int index = 0;
		int num = 0;
		int cnt = n;
		while(num < n - 1){
			if(index + m > cnt){
	        	if((index + m) % cnt == 0)
	            	index = cnt - 1;
	        	else
	            	index = (index + m) % cnt - 1;
	        }else index = index + m - 1;
	        cnt--;
	        num++;
	        q.erase(q.begin() + index);
	  	}
	  	cout<<q[0]<<endl;
	}
	return 0;
}

单调队列

#include <bits/stdcpp.h>
using namespace std;
const int MAXN = 1000000 + 10;
int a[MAXN],l=1,r=0,k,n,mians,maans,q[2*MAXN];		//q数组存a数组元素下标
int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> k;
    for(int i=1; i<=n; i++)
        cin >> a[i];
	//求最小
    for(int i=1; i<=n; i++) {
        while(q[l] < i-k+1 && l <= r) ++l;	//保证队列长度不大于k
        while(a[i] <= a[q[r]] && l <= r) --r;	//更新队列
        q[++r] = i;
        if(i >= k)
            cout << a[q[l]] << " ";		//输出队列中最小值
    }
    cout << endl;
    l = 1, r = 0;
	//求最大
    memset(q,0,sizeof(q));
    for(int i=1; i<=n; i++) {
        while(q[l] < i-k+1 && l <= r) ++l;
        while(a[i] >= a[q[r]] && l <= r) --r;
        q[++r] = i;
        if(i >= k)
            cout << a[q[l]] << " ";
    }

    return 0;
}

字符串里取数字

string st = "123 456 789";
stringstream s(st);
int a,b,c;
s >> a;		//a就被赋值为123
s >> b;		//b就被赋值为456
s >> c;		//c就被赋值为789

KMP

普通next数组
void getnext(){
	int j,k;
	j=0;
	k=-1;
	Next[0]=-1;
	int le = strlen(a);
	while(j<le){
		if(k==-1||t[j]==t[k]) Next[++j]=++k;you
		else k=Next[k];
	}
} 
对应匹配(这个我们可以求出主串中有多少子串)
int kmpcount(){
	int ans=0;
	int i=0;
	int j=0;
	if(la==1&&lb==1){//一处优化
		if(s[0]==t[0])
		return 1;
		else return 0;
	}
	getnext();
	for(i=0;i<la;i++){
		while(j>0&&s[i]!=t[j]) j=Next[j];
		if(s[i]==t[j]) j++;
		if(j==lb){
			ans++;
			j=Next[j];
		}
	}
	return ans;
}
优化Next数组
void getnext(){
    int le=strlen(p);
    Next[0]=-1;
    int k=-1;
    int j=0;
    while(j<le){
        if(k==-1||p[j]==p[k]){
            j++;
            k++;
            if(p[j]!=p[k]) Next[j]=k;
            else Next[j]=Next[k];
        }
        else k=Next[k];
    }
    return;
}
对应匹配
int KMP()
{
	int i=0;
	int j=0;
	int lens=strlen(s);
	int lenp=strlen(p);
	while(i<lens&&j<lenp){
		//如果j=-1(表示第一次开始或者重新开始匹配),即失配于首字符
		if(j==-1||s[i]==p[j]){
			j++;
			i++;
		}
		else{
			//如果j!+-1,或者当前匹配失败 ,则 
			j=Next[j]; // i不变,j=next[j] 
		}
        //if(j==lenp) j = Next[j];	//如果匹配多个子串,上面的循环条件也要改变
	}
	if(j==lenp)
	return 1;//匹配成功 
	else
	return 0; 
}

字典树

y总写法

#include <bits/stdcpp.h>
using namespace std;
const int N = 1e5+10;
int son[N][26],cnt[N],idx;
char s[N];
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 quary(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()
{
    int n;scanf("%d",&n);
    while (n--){
        char op[2];
        scanf("%s%s",op,s);
        if(*op=='I')insert(s);
        else printf("%d\n",quary(s));
    }
    return 0;
}

链表写法:

#include <bits/stdcpp.h>
using namespace std;
struct trie{
	int ncount;
	trie* next[26];
	trie(){		//构造函数,进行初始化
		for(int i = 0; i < 26; i++)
			next[i]=NULL;
		ncount = 0;
	}
};
trie root;
void insert(char word[]){
	trie* r = &root;
	for(int i = 0; word[i]; i++){
		if(r->next[word[i]-'a']==NULL)
			r->next[word[i]-'a'] = new trie;
		r = r->next[word[i]-'a'];
		r->ncount++;//字符数量加一
	}
}
int serch(char word[]){
	trie* r = &root;
	for(int i = 0; word[i]; i++){
		if(r->next[word[i]-'a']==NULL)return 0;
		r=r->next[word[i]-'a'];
	}
	return r->ncount;//到此字符一共有多少个单词
}
int main(){
	char word[11];
	while(gets(word)&&word[0]){	//当word[0]==NULL就直接退出了。
		insert(word);
	}
	while(gets(word)){
		printf("%d\n",serch(word));
	}
	return 0;
} 

LeetCode版本
在这里插入图片描述

struct TrieNode {			//字典树节点
    string word = "";
    unordered_map<char,TrieNode *> children;
};

void insertTrie(TrieNode * root,const string & word) {//添加字符串进树
    TrieNode * node = root;
    for (auto c : word){
        if (node->children[c]==NULL) {
            node->children[c] = new TrieNode();	  //为空,需要创建节点
        }
        node = node->children[c];
    }
    node->word = word;
}
class Solution {
public:
    int tmp[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    int n,m;
    bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root, vector<string> & res) {
        char ch = board[x][y];        
        if (root->children[ch] == NULL) return false;

        root = root->children[ch];
        if (root->word.size() > 0){
            res.push_back(root->word);
            root->word="";		//找到了,就把这个字符串从树种去除
        }

        board[x][y] = '#';		//搜索过得本轮递归就不能在使用
        for (int i = 0; i < 4; ++i) {
            int x_ = x + tmp[i][0];
            int y_ = y + tmp[i][1];
            if (x_ >= 0 && x_ < n && y_ >= 0 && y_ < m && board[x_][y_] != '#')
                dfs(board, x_, y_, root,res);	//递归
        }
        board[x][y] = ch;		//复原
        return true;
    }
    vector<string> findWords(vector<vector<char>> & board, vector<string> & words) {
        TrieNode * root = new TrieNode();
        vector<string> res;
        n = board.size(),m = board[0].size();
        for (auto & word: words) insertTrie(root,word);
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                dfs(board, i, j, root, res);    
        return res;        
    }
};

马拉车算法

//马拉车算法O(N)时间复杂度 
#include <bits/stdcpp.h>
using namespace std;
string Init(string s){
	string st = "*#";//以*#开头,#结尾
	for(int i = 0; i < s.size(); i++){
		st += s[i];
		st += '#';
	}
	return st;
} 
string Manacher(string s){
	string s1 = Init(s);
	int idd = 0, max = 0,len[1000],maxlen = 0,start = 0; // idd是目前最大的len的下标,len[idd]+i=max len[i]表示i到最远的回文长度, 
	for(int i = 0; i < s1.length(); i++){				// max 是最大回文串的右下标的下一个 ,原回文长度就是len[i]-1 
		if(i < max) len[i] = min(len[2*idd-i],max-i);	//idd-(i-idd)
		else len[i] = 1; //i在max右边,直接暴力
		for(;s1[i+len[i]]==s1[i-len[i]]&&i+len[i]<s1.size()&&i-len[i]>0;len[i]++);//暴力求解 
		if(len[i]+i > max){ //更新max和idd的值
			max = len[i]+i;
			idd = i;
		}
		if(len[i]-1 > maxlen){
			maxlen = len[i]-1; //len[i]-1为原来字符串的回文最大长度 
			start = (idd-maxlen)/2; //原来字符串的回文起始位置(idd先变回原字符串位置,再减去长度一半即可) 
		}
	} 
	return s.substr(start,maxlen);	//substr复制子字符串 
}	
int main(){
	string st; cin >> st;
	cout << Manacher(st) << endl;
	return 0;
} 

字符串哈希

#include <bits/stdcpp.h>
using namespace std;
typedef unsigned long long ull;
const int N = 100010, P = 131;
int n, m;
char str[N];
ull h[N], p[N];
ull get(int l, int r){
    return h[r] - h[l - 1] * p[r - l + 1];
}
void init(){
    p[0] = 1;
    for (int i = 1; i <= n; i ++ ){
        h[i] = h[i - 1] * P + str[i];
        p[i] = p[i - 1] * P;
    }
}
int main(){
    scanf("%d%d", &n, &m);
    scanf("%s", str + 1);
    init();
    while (m -- ){
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        if (get(l1, r1) == get(l2, r2)) puts("Yes");
        else puts("No");
    }
    return 0;
}

博弈论

Nim游戏

给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。问如果两人都采用最优策略,先手是否必胜?

结论:如果a1^a2^a3^...^an!=0一定必胜,反之一定必败

台阶-Nim游戏

现在,有一个n级台阶的楼梯,每级台阶上都有若干个石子,其中第i级台阶上有ai个石子(i≥1)。两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。问如果两人都采用最优策略,先手是否必胜

结论:如果奇数项a1^a3^...^an!=0一定必胜,反之一定必败


位运算

1.求n的二进制的第k位数字:n >> k & 1;
//19 10011
cout << (19 >> 4 & 1) << endl;//1
cout << (19 >> 3 & 1) << endl;//0
cout << (19 >> 2 & 1) << endl;//0
cout << (19 >> 1 & 1) << endl;//1
cout << (19 >> 0 & 1) << endl;//1
2.求n的二进制的最后一位1的位置lowbit(n) = n&-n;
//20 10100
cout << (20&-20) << endl;//4

数字转换为十六进制数

32位二进制可以表示8位16进制数
原码 + 取反 + 1 = 补码
补码 + 取反 + 1 = 原码

class Solution {
public:
    string toHex(int num) {
        if (num == 0) return "0";
        string sb;
        for (int i = 7; i >= 0; i --) {
            int val = (num >> (4 * i)) & 0xf;   //一次取4位进行比较
            if (sb.length() > 0 || val > 0) {	//去除前导零
                char digit = val < 10 ? (char) ('0' + val) : (char) ('a' + val - 10);
                sb.push_back(digit);
            }
        }
        return sb;
    }
};

快读

int read(){
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值