目录
根据数据范围推算法
常用的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;
}