常见结论:
循环读入带空格的字符串,直到字符串为空,结束输入:
string input;
while (true) {
cout << "请输入字符串(输入为空时将停止):" << endl;
getline(cin, input);
if (input.empty()) {
break;
}
// 在这里处理读取到的字符串
cout << "输入的字符串是:" << input << endl;
}
cout << "循环结束" << endl;
1.快速排序
void quick_paixu(int q[],int l,int r){
if(l>=r)return;
int x=q[l+r>>1],i=l-1,j=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_paixu(q,l,j),quick_paixu(q,j+1,r);
}
2.归并排序
void guibin(int q[],int l,int r) {
if (l >= r)return;
int mid= l + r >> 1;//定义为中间的数。
guibin(q, l, mid);//处理左边
guibin(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 (int i = l, j = 0; i <= r; i++, j++)q[i] = tmp[j];//将结果存入q数组
}
3.二分
int SL(int l, int r, int x) {//左边界
while (l < r) {
int mid = l + r >> 1;
if (q[mid] >= x) r = mid;
else l = mid + 1 ;
}
return l;
}
int SR (int l, int r, int x) {//右边界
while (l < r) {
int mid = l + r + 1 >> 1;
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
return r;
}
4.前缀和
一维前缀和:
求和:s[i]=s[i-1]+a[i];
求l到r部分:s[r]-s[l-1];
二维前缀和:
求和:s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
求(x1,y1)到(x2,y2)部分:s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
5.差分
一维差分:
在l和r部分加c:a[l]+=c;a[r+1]-=c;
二维差分:
在(x1,y1)和(x2,y2)部分加c:a[x1][y1]+=c;a[x2+1][y1]-=c;a[x1][y2+1]-=c;a[x2+1][y2+1]+=c;
6.二进制中1的个数
long long lowbit(long long x){//返回x二进制中最后一位1
return x&-x;
}
int main(){
long long x;
scanf("%lld",&x);
int ans=0;
while(x){
x-=lowbit(x);
ans++;
}
cout<<ans;
return 0;
}
7.单链表
const int N=1e6+10;
int h[N], e[N], ne[N], head, idx;
int n;
void init(){
head=-1;
idx=0;
}
void int_to_head(int x){
e[idx]=x;
ne[idx]=head;
head=idx;
idx++;
}
void add(int k,int x){
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx;
idx++;
}
void remove(int k){
ne[k] = ne[ne[k]];
}
8.双链表
const int N = 1e5 + 10;
int m;
int e[N], l[N], r[N];
int idx;
void init() {
l[1] = 0, r[0] = 1;
idx = 2;
}
//在第k个点右边插入一个x
void add(int k, int x) {
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
l[r[k]] = idx;
r[k] = idx++;
}
void remove(int k) {
r[l[k]] = r[k];
l[r[k]] = l[k];
}
9.模拟栈
const int N = 1e5 + 10;
int stk[N],tt;
//从栈顶弹出一个数
void pop() {
tt--;
}
bool empty() {
if (tt > 0)cout << "NO" << endl;
else cout<<"YES"<<endl;
}
void query() {
cout << stk[tt] << endl;
}
void push(int x) {
stk[++tt] = x;
}
10.模拟队列
const int N = 1e5 + 10;
int ll = 0, rr = 1,a[N];
void push(int x) {
a[rr++] = x;
}
void pop() {
ll++;
}
void empty() {
if (rr - ll == 1)cout << "YES" << endl;
else cout << "NO" << endl;
}
void query() {
cout << a[ll + 1] << endl;
}
11.单调栈
#include<iostream>
using namespace std;
const int N=1e5+5;
int a[N];
int main(){
int n;
cin>>n;
int tt=0;
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
while(tt&&a[tt]>=x)tt--;
if(tt)cout<<a[tt]<<" ";
else cout<<"-1"<<" ";
a[++tt]=x;
}
return 0;
}
12.滑动窗口
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],b[N],hh,tt=-1;
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
if(i-k+1>b[hh])++hh;
while(hh<=tt&&a[i]<=a[b[tt]])--tt;
b[++tt]=i;
if(i+1>=k)printf("%d ",a[b[hh]]);
}
cout<<endl;
hh=0,tt=-1;
for(int i=0;i<n;i++){
if(i-k+1>b[hh])++hh;
while(hh<=tt&&a[i]>=a[b[tt]])--tt;
b[++tt]=i;
if(i+1>=k)printf("%d ",a[b[hh]]);
}
}
12.kmp字符串
#include<iostream>
using namespace std;
const int N=1e5+10,M=1e6+10;
char a[N],b[M];
int ne[N];
int main(){
int n,m;
cin>>n>>a+1>>m>>b+1;
for(int i=2,j=0;i<=n;i++){
while(j&&a[i]!=a[j+1])j=ne[j];
if(a[i]==a[j+1])j++;
ne[i]=j;
}
for(int i=1,j=0;i<=m;i++){
while(j&&b[i]!=a[j+1])j=ne[j];
if(b[i]==a[j+1])j++;
if(j==n){
cout<<i-n<<' ';
j=ne[j];
}
}
}
13.Trie字符串统计
int son[N][26],cnt[N],idx=0;
char str[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 query(char *str){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u])return 0;
else p=son[p][u];
}
return cnt[p];
}
14.最大异或对
x>>i&1; //取X的第i位的二进制数是什么
#include<iostream>
#include<algorithm>
using namespace std;
const int N =1e5+10, M = N * 31;
int n, a[N], son[M][2],idx;
void insert(int x) {
int p=0;
for(int i=30;i>=0;i--){
int u=x>>i&1;
if(!son[p][u])son[p][u]=++idx;
p=son[p][u];
}
}
int search(int x) {
int p = 0, res = 0;
for (int i = 30; i >= 0; i--) {
int u = x >> i & 1;
if (son[p][!u]) {
p = son[p][!u];
res = res * 2 + 1;
}
else {
p = son[p][u];
res = res * 2 + 0;
}
}
return res;
}
int main() {
cin >> n;
int maxanswer = -1;
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
insert(a[i]);
maxanswer = max(maxanswer, search(a[i]));
}
cout << maxanswer << endl;
}
15.并查集
1.将两个集合合并
2.查询两个元素是否在一个集合当中
基本原理:每个集合用一棵树来表示,树根的编号就是每个集合的编号,每个节点存储它的父节点,p[x]表示x的父节点
问题1:如何判断树根?(p[x]==x)
#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N];
int find(int x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)p[i]=i;
while(m--){
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0]=='M'){
p[find(a)]=find(b);
}
else{
if(find(a)==find(b))printf("Yes\n");
else printf("No\n");
}
}
}
16.堆
STL中的堆:头文件:#include<queue>
大根堆:priority_queue<int>q;
小根堆:priority_queue<int,vector<int>,greater<int>>q;
q.top()//取得堆顶元素,并不会弹出 q.pop()//弹出堆顶元素 q.push()//往堆里面插入一个元素 q.empty()//查询堆是否为空,为空则返回1否则返回0 q.size()//查询堆内元素数量
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int h[N],num;
void down(int x) {
int t = x;
if (2 * x <= num && h[t] >h[x * 2])t = x * 2;
if (2 * x +1<= num && h[t] >h[x * 2+1])t = x * 2+1;
if (t != x) {
swap(h[t], h[x]);
down(t);
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%d", &h[i]);
num = n;
for (int i = n / 2; i; i--)down(i);
while (m--) {
printf("%d ", h[1]);
h[1] = h[num];
num--;
down(1);
}
}
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int h[N],ph[N],hp[N],num;
void heap_swap(int a,int b){
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
void down(int x) {
int t = x;
if (2 * x <= num && h[t] >h[x * 2])t = x * 2;
if (2 * x +1<= num && h[t] >h[x * 2+1])t = x * 2+1;
if (t != x) {
heap_swap(t,x);
down(t);
}
}
void up(int x){
while(x/2&&h[x/2]>h[x]){
heap_swap(x/2,x);
x/=2;
}
}
int main() {
int n, m=0;
scanf("%d",&n);
int k,x;
while(n--){
char op[10];
scanf("%s",op);
if(!strcmp(op,"I")){
scanf("%d",&x);
num++;m++;
ph[m]=num,hp[num]=m;
h[num]=x;
up(num);
}
else if(!strcmp(op,"PM")){
printf("%d\n",h[1]);
}
else if(!strcmp(op,"DM")){
heap_swap(1,num);
num--;
down(1);
}
else if(!strcmp(op,"D")){
scanf("%d",&k);
k=ph[k];
heap_swap(k,num);
num--;
down(k),up(k);
}
else{
scanf("%d%d",&k,&x);
k=ph[k];
h[k]=x;
down(k),up(k);
}
}
}
17.哈希表
维护一个集合,支持如下几种操作:
I x
,插入一个数 x;Q x
,询问数 x 是否在集合中出现过;
//拉链法
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e5 + 3; // 取大于1e5的第一个质数,取质数冲突的概率最小 可以百度
//* 开一个槽 h
int h[N], e[N], ne[N], idx; //邻接表
void insert(int x) {
// c++中如果是负数 那他取模也是负的 所以 加N 再 %N 就一定是一个正数
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx++;
}
bool find(int x) {
//用上面同样的 Hash函数 讲x映射到 从 0-1e5 之间的数
int k = (x % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i]) {
if (e[i] == x) {
return true;
}
}
return false;
}
int n;
int main() {
cin >> n;
memset(h, -1, sizeof h); //将槽先清空 空指针一般用 -1 来表示
while (n--) {
string op;
int x;
cin >> op >> x;
if (op == "I") {
insert(x);
} else {
if (find(x)) {
puts("Yes");
} else {
puts("No");
}
}
}
return 0;
}
//开放寻址法
#include <cstring>
#include <iostream>
using namespace std;
//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了
const int N = 2e5 + 3; //大于数据范围的第一个质数
const int null = 0x3f3f3f3f; //规定空指针为 null 0x3f3f3f3f
int h[N];
int find(int x) {
int t = (x % N + N) % N;
while (h[t] != null && h[t] != x) {
t++;
if (t == N) {
t = 0;
}
}
return t; //如果这个位置是空的, 则返回的是他应该存储的位置
}
int n;
int main() {
cin >> n;
memset(h, 0x3f, sizeof h); //规定空指针为 0x3f3f3f3f
while (n--) {
string op;
int x;
cin >> op >> x;
if (op == "I") {
h[find(x)] = x;
} else {
if (h[find(x)] == null) {
puts("No");
} else {
puts("Yes");
}
}
}
return 0;
}
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2]这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
#include<iostream>
using namespace std;
typedef unsigned long long ULL;
const int N=1e5+10,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];
}
int main(){
scanf("%d%d%s",&n,&m,str+1);
p[0]=1;
for(int i=1;i<=n;i++){
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+str[i];
}
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");
}
}