2、双指针
1、i往后面扫描,当i指向的数字小于等于x,就继续往后,当指向的数字大于x的时候停下
2、此时j往前面扫描,当j指向的数字大于x,就继续往前,当指向的数字小于x的时候,停下
3、此时i指向的大于x,j指向的小于x,那么这两个数字交换,并且两个指针都往中间移动一位,直到i和j穿过
可以发现,任何时候,i右边的数字都小于等于x,j左边的数字都大于x
模板:
void quick_sort(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_sort(q,l,j);
quick_sort(q,j+1,r);
}
快速排序
https://www.acwing.com/problem/content/787/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int q[N];
void quick_sort(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_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main(){
int n;
scanf(“%d”,&n);
for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);
quick_sort(q,0,n-1);
for(int i = 0;i < n;i++) printf("%d ",q[i]);
return 0;
}
第k个数
https://www.acwing.com/problem/content/788/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],n,k;
int quick_sort(int l,int r,int k){
if(l == r) return a[l];
int i = l - 1,j = r + 1,x = a[(l+r) >> 1];
while(i < j){
while(a[++ i] < x); // 这里只能是++i,不能是i–
while(a[-- j] > x);
if(i < j) swap(a[i],a[j]);
}
int sl = j - l + 1;
if(k <= sl) return quick_sort(l,j,k);
else return quick_sort(j+1,r,k-sl);
}
int main(){
scanf(“%d%d”,&n,&k);
for(int i = 0 ;i < n;i++) scanf(“%d”,&a[i]);
printf(“%d\n”,quick_sort(0,n-1,k));
return 0;
}
快速选择算法
归并排序
如何归并
双指针
归并的思路:
1、先递归,将序列一直分半,直到只剩一个元素
2、归并,归并采取双指针
模板:
void merge_sort(int q[],int l,int r){
if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了
int mid = (l + r) >> 1; // mid是中间那个数字
merge_sort(q,l,mid); // 归并排序先递归
merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了
int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点
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];
}
归并排序
https://www.acwing.com/problem/content/789/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int q[N],tmp[N],n;
void merge_sort(int q[],int l,int r){
if(l >= r) return; // 如果区间只有一个或者没有数字,不用递归了
int mid = (l + r) >> 1; // mid是中间那个数字
merge_sort(q,l,mid); // 归并排序先递归
merge_sort(q,mid+1,r); // 分到最后就只剩一个数字了
int k = 0,i = l,j = mid + 1; // 归并,使用双指针,i指向左半边的起点,j指向右半边的起点
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 main(){
scanf(“%d”,&n);
for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);
merge_sort(q,0,n-1);
for(int i = 0;i < n;i++) printf("%d ",q[i]);
return 0;
}
逆序对的数量
https://www.acwing.com/problem/content/790/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],temp[N];
long long merge_sort(int l,int r){
if(l >= r) return 0;
int mid = (l + r) >> 1;
long long res = merge_sort(l,mid) + merge_sort(mid+1,r);
// 归并
int k = 0,i = l,j = mid + 1;
while(i <= mid && j <= r) {
if(a[i] <= a[j]) temp[k++] = a[i++];
else {
temp[k++] = a[j++];
res += mid - i + 1;
}
}
while(i <= mid) temp[k++] = a[i++];
while(j <= r) temp[k++] = a[j++];
for(i = l,k = 0;i <= r;i++,k++) a[i] = temp[k];
return res;
}
int main(){
int n;
scanf(“%d”,&n);
for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);
printf(“%lld\n”,merge_sort(0,n-1));
return 0;
}
二分
模板:https://www.acwing.com/blog/content/31/
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;
}
数的范围
https://www.acwing.com/problem/content/791/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int q[N],m,n;
int main(){
scanf(“%d %d”,&n,&m);
for(int i = 0;i < n;i++) scanf(“%d”,&q[i]);
while(m–){
int x;
scanf(“%d”,&x);
// 先不管check函数,先把l,r和while,mid写好
int l = 0,r = n - 1;
while(l < r){
int mid = (l + r) >> 1;
if(q[mid] >= x) r = mid;
else l = mid + 1;
}
if(q[l] != x) puts(“-1 -1”);
else{
printf("%d ",l);
l = 0,r = n - 1;
while(l < r){
int mid = (l + r + 1) >> 1;
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
printf(“%d\n”,l);
}
}
return 0;
}
浮点数二分
开平方
#include <bits/stdc++.h>
using namespace std;
int main(){
double x;
cin >> x;
double l = 0,r = x;
while(r - l > 1e-6){
double mid = (l + r) / 2;
if(mid * mid >= x) r = mid;
else l = mid;
}
printf(“%lf\n”,l);
return 0;
}
数的三次方根
https://www.acwing.com/problem/content/792/
#include <bits/stdc++.h>
using namespace std;
int main(){
double n;
scanf(“%lf”,&n);
double l = -10000,r = 10000;
while(r-l > 1e-8){
double mid = (l + r) / 2;
if(mid * mid * mid > n) r = mid;
else l = mid;
}
printf(“%lf\n”,l);
return 0;
}
高精度
前缀和
一维前缀和
前缀和
https://www.acwing.com/problem/content/797/
预处理:O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],s[N];
int main(){
int n,m;
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= n;i++) {
scanf(“%d”,&a[i]);
s[i] = s[i-1] + a[i]; // 前缀和数组的初始化
}
while(m–){
int l,r;
scanf(“%d%d”,&l,&r);
printf(“%d\n”,s[r]-s[l-1]);
}
return 0;
}
二维前缀和
S[i,j]的含义:这个格子左上角面积之和
S[i,j]计算方法:
(x1,y1),(x2,y2)子矩阵中所有数之和怎么计算?
子矩阵的和
https://www.acwing.com/problem/content/798/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int a[N][N],s[N][N];
int main(){
int n,m,q;
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]);
// 初始化二维前缀和
s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
}
}
while(q–){
int x1,y1,x2,y2;
scanf(“%d%d%d%d”,&x1,&y1,&x2,&y2);
// 计算差
printf(“%d\n”,s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]);
}
return 0;
}
差分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z6zhrdDJ-1630933432550)(https://gitee.com/Crescent_P/picture-bed/raw/master/hh.png)]差分数组不需要构造
一维差分
https://www.acwing.com/problem/content/799/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N];
// 插入函数,使得b[l] + c,b[r+1] - c
void insert(int l,int r,int c){
b[l] += c;
b[r + 1] -= c;
}
int main(){
int n,m;
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= n;i++){
scanf(“%d”,&a[i]);
// 差分数组不需要构建,先假设a数组为全零,那么b数组也就为全零
// [1,1]插入a[1],[2,2]插入a[2]
// 就能构造出差分数组
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++) {
a[i] = a[i-1] + b[i];
printf("%d ",a[i]);
}
}
二维差分
差分矩阵
https://www.acwing.com/problem/content/800/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int a[N][N],b[N][N];
// 插入公式
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1] += c;
b[x1][y2+1] -= c;
b[x2+1][y1] -= c;
b[x2+1][y2+1] += c;
}
int main(){
int n,m,q;
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]);
// 构建差分数组b
insert(i,j,i,j,a[i][j]);
}
}
while(q–){
int x1,y1,x2,y2,c;
scanf(“%d%d%d%d%d”,&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++){
a[i][j] = b[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1];
printf("%d ",a[i][j]);
}
printf(“\n”);
}
return 0;
}
双指针算法
输出单词
#include <bits/stdc++.h>
using namespace std;
int main(){
char str[1000];
gets(str);
int n = strlen(str);
for(int i = 0;i < n;i++){
int j = i;
while(j < n && str[j] != ’ ') j++;
// 这道题的具体逻辑
for(int k = i;k < j;k++) printf(“%c”,str[k]);
puts(“”);
i = j;
}
return 0;
}
最长连续不重复子序列
https://www.acwing.com/problem/content/801/
暴力:O(n^2)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N];
int main(){
int n;
scanf(“%d”,&n);
for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);
int ans = 0;
for(int i = 0;i < n;i++){
b[a[i]]++;
for(int j = i+1;j < n;j++){
if(b[a[j]] != 0) break;
b[a[j]]++;
ans = max(ans,j - i + 1);
}
b[a[i]]–;
for(int i = 0;i < n;i++) b[i] = 0;
}
printf(“%d\n”,ans);
return 0;
}
双指针:O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N];
int main(){
int n;
scanf(“%d”,&n);
for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);
int ans = 0;
for(int i = 0,j = 0;i < n;i++){
while(j < n && b[a[j]] == 0){
ans = max(ans,j - i + 1);
b[a[j++]]++;
}
b[a[i]]–;
}
printf(“%d\n”,ans);
return 0;
}
数组元素的目标和
https://www.acwing.com/problem/content/802/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N],n,m,x;
int main(){
scanf(“%d%d%d”,&n,&m,&x);
for(int i = 1;i <= n;i++) scanf(“%d”,&a[i]);
for(int j = 1;j <= m;j++) scanf(“%d”,&b[j]);
for(int i = 1,j = m;i <= n ;i++){
while(j <= m && a[i] + b[j] > x) j–;
if(a[i] + b[j] == x) {
printf(“%d %d”,i-1,j-1);
break;
}
}
return 0;
}
判断子序列
https://www.acwing.com/problem/content/description/2818/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int a[N],b[N];
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= n;i++) scanf(“%d”,&a[i]);
for(int j = 1;j <= m;j++) scanf(“%d”,&b[j]);
bool flag = true;
for(int i = 1,j = 1;i <= m && flag;i++){
while(j <= n && b[i] == a[j]) j++,i++;
// 移动到最后一位后面了
if(j == n+1) flag = false;
}
if(flag) puts(“No”);
else puts(“Yes”);
return 0;
}
位运算
可以求出10的二进制表示
#include <bits/stdc++.h>
using namespace std;
int main(){
int n = 10;
for(int k = 3;k >= 0;k-- ) printf(“%d”,(n>>k)&1);
return 0;
}
lowbit运算
二进制中1的个数
https://www.acwing.com/problem/content/803/
#include <bits/stdc++.h>
using namespace std;
int lowbit(int x){
return x & -x;
}
int main(){
int n;
scanf(“%d”,&n);
while(n–){
int x;
scanf(“%d”,&x);
int res = 0;
// 每次减去x的最后一位1
while(x) x -= lowbit(x), res++;
printf("%d ",res);
}
return 0;
}
离散化
区间和
https://www.acwing.com/problem/content/804/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 3e5 + 10;
int n,m;
int a[N],s[N];
vector alls;
vector add,query;
// 通过x的值找到下标,将一个值序列,映射到从1开始的数组
int find(int x){
int l = 0,r = alls.size() - 1;
while(l < r){
int mid = (l + r) >> 1;
if(alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 0;i < n;i++){
int x,c;
scanf(“%d%d”,&x,&c);
add.push_back({x,c});
alls.push_back(x);
}
for(int i = 0;i < m;i++){
int l,r;
scanf(“%d%d”,&l,&r);
query.push_back({l,r});
alls.push_back(l);
alls.push_back®;
}
// 去重
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
// 上面操作完,是一个去重并且排序后的一个数组
// 处理插入
for(auto item: add) {
int x = find(item.first);
// 上面x是通过值找到的下标,x是下标
a[x] += item.second;
}
// 预处理前缀和
for(int i= 1;i <= alls.size();i++) s[i] = s[i-1] + a[i];
// 处理询问
for(auto item : query) {
int l = find(item.first);
int r = find(item.second);
printf(“%d\n”,s[r] - s[l-1]);
}
return 0;
}
区间合并
区间合并
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2x7KVK5l-1630933432558)(算法基础课.assets/image-20210830163830687.png)]
https://www.acwing.com/problem/content/805/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int n;
vector segs;
void merge(vector &segs){
vector res;
// 以左端点排序
sort(segs.begin(),segs.end());
int st = -2e9,ed = -2e9;
for(auto seg : segs){
// 没有交集
if(ed < seg.first){
if(st != -2e9) res.push_back({st,ed});
st = seg.first,ed = seg.second;
}else ed = max(ed,seg.second);
}
// 最后一个
if(st != -2e9) res.push_back({st,ed});
segs = res;
}
int main(){
scanf(“%d”,&n);
for(int i = 0;i < n;i++){
int l,r;
scanf(“%d%d”,&l,&r);
segs.push_back({l,r});
}
merge(segs);
printf(“%d\n”,segs.size());
return 0;
}
单链表
用数组模拟链表
e数组存值,ne数组存next
单链表
https://www.acwing.com/problem/content/828/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// 存储到当前用到了哪个点
int head,inx,e[N],ne[N];
// 初始化
void init(){
// 初始化,链表为空,-1表示空集
head = -1;
inx = 0;
}
// 将x插到头结点,就是让x成为头结点
void add_to_head(int x) {
e[inx] = x;
ne[inx] = head;
// head 表示头结点的下标,head不是一个节点哦
head = inx;
inx++;
}
// 将x插入到下标是k的节点的后面
void add(int k,int x){
e[inx] = x;
ne[inx] = ne[k];
ne[k] = inx;
inx++;
}
// 将下标是k的点的后面的点删除
void remove(int k){
ne[k] = ne[ne[k]];
}
int main(){
int M;
scanf(“%d”,&M);
init();
while(M–){
int k,x;
char op;
cin >> op;
if(op == ‘H’){
scanf(“%d”,&x);
add_to_head(x);
}else if(op == ‘D’){
scanf(“%d”,&k);
// 删除头结点,就是让head指向头结点的下一个
if (!k) head = ne[head];
else remove(k-1);
}else{
scanf(“%d%d”,&k,&x);
add(k-1,x);
}
}
for(int i = head;i != -1;i = ne[i]) printf("%d ",e[i]);
return 0;
}
双链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jF276k9j-1630933432561)(算法基础课.assets/image-20210831153743826.png)]
双链表
https://www.acwing.com/problem/content/829/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int e[N],l[N],r[N],idx;
// 初始化
void init(){
// 0 表示右端点
// 1 表示左端点
r[0] = 1,l[1] = 0;
idx = 2;
}
// 在下标是k的点的右边,插入x
void add(int k,int x){
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
r[k] = idx;
l[r[idx]] = idx;
idx++;
}
// 删除第k个点
void remove(int k) {
r[l[k]] = r[k];
l[r[k]] = l[k];
}
int main(){
int M;
scanf(“%d”,&M);
init();
while(M–){
int x,k;
string op;
cin >> op;
if(op == “L”){
scanf(“%d”,&x);
add(0,x);
}else if(op == “R”){
scanf(“%d”,&x);
add(l[1],x);
}else if(op == “D”){
scanf(“%d”,&k);
// idx从2开始,第k个点的下标应该是k+1
remove(k+1);
}else if(op == “IL”){
scanf(“%d%d”,&k,&x);
add(l[k+1],x);
}else{
scanf(“%d%d”,&k,&x);
add(k+1,x);
}
}
for(int i = r[0];i != 1;i = r[i]) printf("%d ",e[i]);
return 0;
}
栈
先进后出
模拟栈
https://www.acwing.com/problem/content/830/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// tt 是栈顶坐标
int stk[N],tt;
void push(int x) {
stk[++tt] = x;
}
void pop(){
tt–;
}
bool empty(){
if(tt == 0) return true;
else return false;
}
int query(){
return stk[tt];
}
int main(){
int M;
scanf(“%d”,&M);
while(M–){
string op;
int x;
cin >>op;
if(op == “push”){
scanf(“%d”,&x);
push(x);
}else if(op == “pop”){
pop();
}else if(op == “query”){
printf(“%d\n”,query());
}else{
if(empty()) printf(“YES\n”);
else printf(“NO\n”);
}
}
return 0;
}
队列
先进先出
模拟队列
https://www.acwing.com/problem/content/831/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
// 在队尾插入元素,在队头弹出原始
int q[N],hh,tt = -1;
void push(int x) {
q[++ tt] = x;
}
void pop(){
hh++;
}
bool empty(){
if(hh <= tt) return false;
else return true;
}
int query(){
return q[hh];
}
int main(){
int M;
scanf(“%d”,&M);
while(M–){
string op;
int x;
cin >> op;
if(op == “push”){
scanf(“%d”,&x);
push(x);
}else if(op == “pop”){
pop();
}else if(op == “query”){
printf(“%d\n”,query());
}else{
if(empty()) printf(“YES\n”);
else printf(“NO\n”);
}
}
return 0;
}
单调栈
单调栈
常见模型:找出每个数左边离它最近的比它大/小的数
https://www.acwing.com/problem/content/description/832/
O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
// tt一开始指向0,指向0表示没有元素
int stk[N],tt;
int main(){
scanf(“%d”,&n);
for(int i = 0;i < n;i++){
int x;
scanf(“%d”,&x);
// 如果栈不空,并且栈顶元素大于x,弹栈
while(tt && stk[tt] >= x) tt–;
// 如果栈不空,输出栈顶
if(tt) printf("%d ",stk[tt]);
else printf("-1 ");
// 压栈,因为对于后面的元素而言,这个是离它最近,并且小的数了
stk[++tt] = x;
}
return 0;
}
单调队列
常见模型:找出滑动窗口中的最大值/最小值
base on 董晓 : https://www.bilibili.com/video/BV1H5411j7o6
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3TivCQf-1630933432561)(https://gitee.com/Crescent_P/picture-bed/raw/master/image-20210901142939496.png)]
滑动窗口
https://www.acwing.com/problem/content/156/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N],q[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]);
for(int i = 0;i < n;i++){
// q[hh] 不在[i-k+1,i],队头出队,就是判断队头是否已经滑出了窗口
// 队列中存的是下标
if(hh <= tt && q[hh] < i-k+1) hh++;
// 如果要进来的元素比队列中的元素小,那么队尾元素出队,保证队列的元素是由小到大
// 因为较小的还用的上,大了的就用不上了
while(hh <= tt && a[q[tt]] >= a[i]) tt–;
// 当前元素的下标入队
q[++tt] = i;
// 保证窗口内的元素足够才开始输出
// 队首存的是最小元素的下标
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
puts(“”) ;
hh = 0,tt = -1;
for(int i = 0;i < n;i++){
if(hh <= tt && i-k+1>q[hh]) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt–;
q[++tt] = i;
if(i >= k-1 ) printf("%d ",a[q[hh]]);
}
return 0;
}
KMP
KMP字符串
https://www.acwing.com/problem/content/833/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10,M = 1e5+10;
int n,m;
char p[N],s[M];
int ne[N];
int main(){
// 从第1位开始
cin >> n >> p+1 >> m >> s+1;
// 求next数组
// next[1]=0,因为是j+1来匹配
for(int i = 2,j = 0;i <= n;i++) {
// 如果匹配不成功
while(j && p[i]!=p[j+1]) j = ne[j];
// 匹配成功
if(p[i] == p[j+1]) j++;
ne[i] = j;
}
// kmp匹配过程
for(int i = 1,j = 0;i <= m;i++) {
while(j && s[i] != p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
// 匹配完了
if(j == n){
// 下标从1开始的所以要减去1 ,应该是 i-n+1-1
printf("%d ",i-n);
j = ne[j]; //
}
}
return 0;
}
Trie
高效地存储和查找字符串集合的数据结构
要么全是大写,要么全是小写,要么全是数字,类型不会特别多
Trie字符串统计
https://www.acwing.com/problem/content/837/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// 下标是0的点,既是根节点,又是空节点
int son[N][26], cnt[N],idx;
知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer
2、多线程、高并发、缓存入门到实战项目pdf书籍
3、文中提到面试题答案整理
4、Java核心知识面试宝典
覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入
大了的就用不上了
while(hh <= tt && a[q[tt]] >= a[i]) tt–;
// 当前元素的下标入队
q[++tt] = i;
// 保证窗口内的元素足够才开始输出
// 队首存的是最小元素的下标
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
puts(“”) ;
hh = 0,tt = -1;
for(int i = 0;i < n;i++){
if(hh <= tt && i-k+1>q[hh]) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt–;
q[++tt] = i;
if(i >= k-1 ) printf("%d ",a[q[hh]]);
}
return 0;
}
KMP
KMP字符串
https://www.acwing.com/problem/content/833/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10,M = 1e5+10;
int n,m;
char p[N],s[M];
int ne[N];
int main(){
// 从第1位开始
cin >> n >> p+1 >> m >> s+1;
// 求next数组
// next[1]=0,因为是j+1来匹配
for(int i = 2,j = 0;i <= n;i++) {
// 如果匹配不成功
while(j && p[i]!=p[j+1]) j = ne[j];
// 匹配成功
if(p[i] == p[j+1]) j++;
ne[i] = j;
}
// kmp匹配过程
for(int i = 1,j = 0;i <= m;i++) {
while(j && s[i] != p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
// 匹配完了
if(j == n){
// 下标从1开始的所以要减去1 ,应该是 i-n+1-1
printf("%d ",i-n);
j = ne[j]; //
}
}
return 0;
}
Trie
高效地存储和查找字符串集合的数据结构
要么全是大写,要么全是小写,要么全是数字,类型不会特别多
Trie字符串统计
https://www.acwing.com/problem/content/837/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// 下标是0的点,既是根节点,又是空节点
int son[N][26], cnt[N],idx;
知其然不知其所以然,大厂常问面试技术如何复习?
1、热门面试题及答案大全
面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer
[外链图片转存中…(img-XrlnRs0O-1714779437181)]
2、多线程、高并发、缓存入门到实战项目pdf书籍
[外链图片转存中…(img-zwQDmv3x-1714779437182)]
[外链图片转存中…(img-3ZJDJPOS-1714779437182)]
[外链图片转存中…(img-sxdpn0zX-1714779437182)]
3、文中提到面试题答案整理
[外链图片转存中…(img-gKRmO3xD-1714779437183)]
4、Java核心知识面试宝典
覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入
[外链图片转存中…(img-6Rk7IyHt-1714779437183)]
[外链图片转存中…(img-CZIN3OMp-1714779437183)]
[外链图片转存中…(img-9FifT7fz-1714779437184)]