AcWing 826. 单链表
idx万岁
#include
#include
using namespace std;
const int N = 100010;
int M;
int e[N],ne[N];
int head,idx; //head为头结点的数组下标,idx为即将使用的数组下标
void init(){
memset(e,0,sizeof(e));
memset(ne,0,sizeof(ne));
head = -1;idx = 0;
}
void add_to_head(int x){
e[idx] = x; ne[idx] = head; head = idx; idx++;
}
// 第k次插入的数后面插入x
void add(int k,int x){
e[idx] = x; ne[idx] = ne[k]; ne[k] = idx; idx++;
}
// 删除第k个插入的数后面的一个数
void remove(int k){
ne[k] = ne[ne[k]];
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0);
init();
cin >> M;
while(M--){
char op; cin >> op;
if(op == 'H'){
int x; cin >> x;
add_to_head(x);
}else if(op == 'I'){
int k,x; cin >> k >> x;
add(k-1,x);
}else{
int k; cin >> k;
if(k == 0) head = ne[head];
else remove(k-1);
}
}
for(int k = head; k != -1; k = ne[k]){
cout << e[k] << " ";
}
return 0;
}
ne[0] = -1 存放头结点
#include
#include
using namespace std;
const int N = 100010;
int M;
int e[N],ne[N];
int idx;
void init(){
memset(e,0,sizeof(e));
memset(ne,0,sizeof(ne));
ne[0] = -1; //ne[0],头结点,不存任何东西
idx = 1;
}
void add_to_head(int x){
e[idx] = x; ne[idx] = ne[0]; ne[0] = idx; idx++;
}
// 第k次插入的数后面插入x
void add(int k,int x){
e[idx] = x; ne[idx] = ne[k]; ne[k] = idx; idx++;
}
// 删除第k个插入的数后面的一个数
void remove(int k){
ne[k] = ne[ne[k]];
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
init();
cin >> M;
while(M--){
char op; cin >> op;
if(op == 'H'){
int x; cin >> x;
add_to_head(x);
}else if(op == 'I'){
int k,x; cin >> k >> x;
add(k,x);
}else{
int k; cin >> k;
if(k == 0) ne[0] = ne[ne[0]];
else remove(k);
}
}
for(int k = ne[0]; k != -1; k = ne[k]){
cout << e[k] << " ";
}
return 0;
}
AcWing 827. 双链表
#include
#include
using namespace std;
const int N = 100010;
int e[N],l[N],r[N];
int idx;
int M;
void init(){
memset(e,0,sizeof(e));
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
r[0] = 1; //头指针
l[1] = 0; //尾指针
idx = 2;
}
void add_to_L(int x){
e[idx] = x;
r[idx] = r[0]; // ->[最右] -> <- <-
r[0] = idx;
l[r[idx]] = idx;
l[idx] = 0;
idx++;
}
void add_to_R(int x){
e[idx] = x;
l[idx] = l[1]; // <-[最左] <- -> ->
l[1] = idx;
r[l[idx]] = idx;
r[idx] = 1;
idx++;
}
//表示将第 k 个插入的数删除
void remove(int k){
r[l[k]] = r[k];
l[r[l[k]]] = l[k];
}
void add_to_k_l(int k,int x){
e[idx] = x;
l[idx] = l[k];
l[k] = idx;
r[l[idx]] = idx;
r[idx] = k;
idx++;
}
void add_to_k_r(int k,int x){
e[idx] = x;
r[idx] = r[k];
r[k] = idx;
l[r[idx]] = idx;
l[idx] = k;
idx++;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
init();
cin >> M;
while(M--){
char op; cin >> op;
if(op == 'L'){
int x; cin >> x;
add_to_L(x);
}else if(op == 'R'){
int x; cin >> x;
add_to_R(x);
}else if(op == 'D'){
int k; cin >> k;
remove(k+1);
}else{
char op2; cin >> op2;
if(op2 == 'L'){ //IL
int k,x; cin >> k >> x;
add_to_k_l(k+1,x);
}else{ //IR
int k,x; cin >> k >> x;
add_to_k_r(k+1,x);
}
}
}
for(int k = r[0]; k != 1; k = r[k]){
cout << e[k] << " ";
}
return 0;
}
简化版
#include
#include
using namespace std;
const int N = 100010;
int e[N],l[N],r[N];
int idx;
int M;
void init(){
memset(e,0,sizeof(e));
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
r[0] = 1; //头指针
l[1] = 0; //尾指针
idx = 2;
}
//表示将第 k 个插入的数删除
void remove(int k){
r[l[k]] = r[k];
l[r[l[k]]] = l[k];
}
void add_to_k_r(int k,int x){
e[idx] = x;
r[idx] = r[k];
r[k] = idx;
l[r[idx]] = idx;
l[idx] = k;
idx++;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
init();
cin >> M;
while(M--){
char op; cin >> op;
if(op == 'L'){
int x; cin >> x;
add_to_k_r(0,x);
}else if(op == 'R'){
int x; cin >> x;
add_to_k_r(l[1],x);
}else if(op == 'D'){
int k; cin >> k;
remove(k+1);
}else{
char op2; cin >> op2;
if(op2 == 'L'){ //IL
int k,x; cin >> k >> x;
add_to_k_r(l[k+1],x);
}else{ //IR
int k,x; cin >> k >> x;
add_to_k_r(k+1,x);
}
}
}
for(int k = r[0]; k != 1; k = r[k]){
cout << e[k] << " ";
}
return 0;
}
AcWing 828. 模拟栈
#include
using namespace std;
const int N = 1e5 + 11;
int M;
int stk[N],tt = -1;
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> M;
while(M--){
string str; cin >> str;
if(str == "push"){
int x; cin >> x;
stk[++tt] = x;
}else if(str == "pop"){
--tt;
}else if(str == "query"){
cout << stk[tt] << endl;
}else{
if(tt == -1) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
return 0;
}
#include
#include
using namespace std;
stack<int> st;
int M;
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> M;
while(M--){
string str; cin >> str;
if(str == "push"){
int x; cin >> x;
st.push(x);
}else if(str == "pop"){
st.pop();
}else if(str == "query"){
cout << st.top() << endl;
}else{
if(st.size() == 0) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
return 0;
}
AcWing 3302. 表达式求值
#include
#include
#include
#include
using namespace std;
unordered_map<char,int> cmp = {{'+',1},{'-',1},{'*',2},{'/',2}};
stack<int> op,num;
void eval(){
int a = num.top(); num.pop();
int b = num.top(); num.pop();
char ope = op.top(); op.pop();
int res = 0;
if(ope == '+') res = b+a;
if(ope == '-') res = b-a;
if(ope == '*') res = b*a;
if(ope == '/') res = b/a;
num.push(res);
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
string str; cin >> str;
int n = str.length();
for(int i = 0; i < n; i++){
if(isdigit(str[i])){
int t = 0,j = i;
while(j < n && isdigit(str[j])){
t*= 10;
t += str[j] - '0';
j++;
}
num.push(t);
i = j-1;
}else if(str[i] == '('){
op.push(str[i]);
}else if(str[i] == ')'){
// op栈运算至(位置
while(op.size() && op.top() != '(') eval();
op.pop(); // '('弹出
}else{ // 运算符
//如果用if,0-2*3+2会被计算成-8
//同级运算,先算左边,再算右边,*/算过之后,栈内还有操作符+-,而后面也有操作符+-,前面的+-比当前的*/小,如果前面的不算完,就会可能出错
while(op.size() && cmp[op.top()] >= cmp[str[i]])//栈内的先算一个 a op b -> num c
eval();
op.push(str[i]);
}
}
while(op.size()) eval();
cout << num.top() << endl;
return 0;
}
AcWing 829. 模拟队列
#include
#include
using namespace std;
int M;
const int N = 100010;
int q[N],tt = -1, hh;
int main(){
cin >> M;
while(M--){
string s; cin >> s;
if(s == "push"){
int x; cin >> x;
q[++tt] = x;
}else if(s == "pop"){
hh++;
}else if(s == "empty"){
if(hh > tt) cout << "YES" << endl;
else cout << "NO" << endl;
}else{
cout << q[hh] << endl;
}
}
return 0;
}
AcWing 830. 单调栈
单调栈:找一个距离该数离他最近且比他大或者小的数
栈可以保持最近
如果栈顶元素比 当前元素小,那么当前元素是距离右边最近的且最小的元素,当前元素入栈
如果栈顶元素比 当前元素大,那么栈里面的元素比当前元素大的元素都要删除
#include
using namespace std;
const int N = 100010;
int stk[N],tt = -1;
int a[N];
int n;
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
for(int i = 0; i < n; i++){
while(tt >= 0 && stk[tt] >= a[i]) tt--;
if(tt < 0) cout << "-1 ";
else cout << stk[tt] << " ";
stk[++tt] = a[i];
}
return 0;
}
#include
#include
using namespace std;
const int N = 100010;
int a[N];
stack<int> stk;
int n;
int main(){
cin >> n;
for(int i = 0; i < n; i++){
cin >> a[i];
}
stk.push(a[0]);
cout << -1 << " ";
for(int i = 1; i < n; i++){
while(stk.size() && stk.top() >= a[i]){
stk.pop();
}
if(stk.empty()) cout << "-1 ";
else cout << stk.top() << " ";
stk.push(a[i]);
}
return 0;
}
AcWing 154. 滑动窗口
if(a[i] >= a[j] && i < j) delete a[i];
队列里面存放下标,可以判断队首元素是否出队列
#include
using namespace std;
const int N = 1000100;
int a[N],q[N];
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int n,k; cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> a[i];
int hh = 0,tt = -1;
for(int i = 1; i <= n; i++){
//判断队头元素是否划出窗口
if(tt >= hh && q[hh] < i-k+1) hh++;
//判断a[i]
while(tt >= hh && a[q[tt]] >= a[i]) tt--;
q[++tt] = i;
if(i >= k) cout << a[q[hh]] << " ";
}
cout << endl;
hh = 0, tt = -1;
for(int i = 1; i <= n; i++){
if(tt >= hh && q[hh] < i-k+1) hh++;
while(tt >= hh && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if(i >= k) cout << a[q[hh]] << " ";
}
cout << endl;
}
AcWing 831. KMP字符串
----------题解----------
最长公共前后缀
next[1] = 0 肯定
so i = 2
开始比较
#include
using namespace std;
int n,m;
const int N = 1000010;
char p[N],s[N];
int ne[N];
int main(){
cin >> n >> p+1 >> m >> s+1;
// p[1~n]
// next[1] = 0;
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++; //j指向下一个
ne[i] = j; //确实是这样
}
// s[1~m]
// j[0~n-1] j+1[1,n]
for(int i = 1,j = 0; i <= m; i++){
while(j && s[i] != p[j+1]) j = ne[j]; //因为p[j] == s[i-1]
// 如果 j = 0被打回原形 j重新从1->n 开始比较
if(s[i] == p[j+1]) j++;
if(j == n){
cout << (i-n+1)-1 << " "; //下标从1开始
j = ne[j]; //继续比较
}
}
return 0;
}
AcWing 835. Trie字符串统计
#include
using namespace std;
const int N = 100010;
int cnt[N]; // cnt[idx] idx节点为终点的字符串个数
int son[N][26]; // 单独一个节点,值存放idx,指针
int p; // 既是根节点也是空节点
int idx; // 节点的下标
int insert(string s){
p = 0;
for(int i = 0; s[i]; i++){
int u = s[i] - '0';
// son就是当前节点
if(!son[p][u]) son[p][u] = ++idx; //son[p][u] 表示当前s[i]节点的 idx下标,其值等于cnt[son[p][u]];
p = son[p][u];
}
cnt[p]++; //是p 不是idx
}
int query(string s){
p = 0;
for(int i = 0; s[i]; i++){
int u = s[i] - '0';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int main(){
int N; cin >> N;
while(N--){
string op,s; cin >> op >> s;
if(op == "I"){
insert(s);
}else{
cout << query(s) << endl;
}
}
return 0;
}
AcWing 142. 前缀统计
#include
using namespace std;
const int N = 1000010;
int son[N][26]; // s[0][u] 就是 s[0]对应的下标
int p,idx;
int cnt[N];
void insert(string s){
p = 0;
// p[0~s.length()-1] i[0~s.length()-1]
for(int i = 0; s[i]; i++){
int u = s[i] - '0';
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u]; //当前下标
}
cnt[p]++;
}
int query(string s){
int res = 0;
p = 0;
for(int i = 0; s[i]; i++){ //son[p][u] 表示当前s[i]节点的 idx下标,其值等于cnt[son[p][u]];
int u = s[i] - '0';
if(!son[p][u]) return res;
p = son[p][u];
if(p > 0 && cnt[p] > 0) res += cnt[p];
}
return res;
}
int main(){
int N,M; cin >> N >> M;
while(N--){
string s; cin >> s;
insert(s);
}
while(M--){
string s; cin >> s;
cout << query(s) << endl;
}
return 0;
}
AcWing 143. 最大异或对
#include
#include
#include
using namespace std;
const int N = 4e6 + 11;
int a[N];
int son[N][2];
int idx,p;
void insert(int x){
p = 0;
for(int i = 31; i >= 1; i--){
int u = (x >> (i-1))&1
//son[p][u]存放的是儿子节点idx
// p = son[p][u] != 0 说明该节点存在,儿子节点存不存在不清楚
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
}
// 找与x异或最大的那个
int query(int x){
p = 0;
int res = 0;
long long k = pow(2,31);
for(int i = 31; i >= 1; i--){
k = k >> 1;
int u = (x >> (i-1))&1;
if(son[p][!u]){
p = son[p][!u]; //走另外一条路
res += k;
}else{
p = son[p][u]; //只有这一条路可走
}
}
return res;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
insert(a[i]);
}
int res = 0;
for(int i = 1; i <= n; i++){
res = max(res,query(a[i]));
}
cout << res << endl;
return 0;
}
在找根节点的过程中,只要找到根节点,那么在寻找根节点的路径中节点的所有值都 = 父节点下标路径压缩
那么查找时间复杂度 ~= O(1)
AcWing 836. 合并集合
没有路径压缩超时
#include
using namespace std;
const int N = 100010;
int fa[N];
int n,m;
int get(int x){
while(fa[x] != x) x = fa[x];
return x;
}
void merge(int a,int b){
// a以及 a的祖先,那一堆,a集合 与 b集合合并
fa[get(a)] = get(b);
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) fa[i] = i;
while(m--){
char op;
int c,d;
cin >> op >> c >> d;
if(op == 'M'){
merge(c,d);
}else{
if(get(c) == get(d)) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
压缩一下
// int get(int x){ // 找 fa[x] == x 的下标 x
// while(fa[x] != x) x = fa[x];
// return x;
// }
int get(int x){ //找父节点的下标 fa[x] 存放的是父节点的下标, x 只是下标,x都不一样
if(fa[x] != x) fa[x] = get(fa[x]);
return fa[x];
}
AcWing 837. 连通块中点的数量
#include
#include
using namespace std;
const int N = 100010;
int fa[N],cnt[N];
int n,m;
int get(int x){
if(fa[x] != x) fa[x] = get(fa[x]);
return fa[x];
}
int merge(int a,int b){
fa[get(a)] = get(b);
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) fa[i] = i,cnt[i] = 1;
while(m--){
string op; cin >> op;
if(op == "C"){
int a,b; cin >> a >> b;
if(get(a) != get(b)){
cnt[get(b)] += cnt[get(a)]; // a和b在同一个集合不需要添加
merge(a,b);
}
}else if(op == "Q1"){
int a,b; cin >> a >> b;
if(get(a) == get(b)) cout << "Yes" << endl;
else cout << "No" << endl;
}else{
int a; cin >> a;
cout << cnt[get(a)] << endl;
}
}
return 0;
}
AcWing 240. 食物链
#include
using namespace std;
const int N = 300010;
int fa[N],d[N]; // cnt存, 编号为x的节点到根节点的距离
int n,k;
int res;
int find(int x){
if(fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
void merge(int a,int b){
fa[find(a)] = find(b);
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> k;
// x 是同类并查集, x+n是捕食并查集, x+2n是天敌并查集 x+2n -吃- x -吃- x+n
for(int i = 1; i <= n*3; i++) fa[i] = i;
while(k--){
int t,x,y; cin >> t >> x >> y;
if(x > n || y > n){
res++;
continue;
}
if(t == 1){ // x 和 y是同类
if(find(x) == find(y+n) || find(x) == find(y+n+n)) res++;
else{ // 添加x节点
merge(x,y);
merge(x+n,y+n);
merge(x+n+n,y+n+n);
}
}else{ // x吃y
if(find(x) == find(y) || find(y+n) == find(x)) res++;
else{
merge(x,y+n+n);
merge(x+n,y);
merge(x+n+n,y+n);
}
}
}
cout << res << endl;
return 0;
}
AcWing 838. 堆排序
#include
using namespace std;
const int N = 100010;
int h[N],si;
int n,m; // n不变,但是 si是变化的!
void down(int u){
int t = u; // t存放当前节点、左孩子、右孩子三者的最小节点下标
if(u * 2 <= si && h[u*2] < h[t]) t = u*2;
if(u * 2 + 1 <= si && h[u*2+1] < h[t]) t = u*2+1;
if(t != u){
swap(h[u],h[t]);
down(t);
}
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> h[i];
si = n;
for(int i = n/2; i >= 1; i--) down(i);
while(m--){
cout << h[1] << " ";
h[1] = h[si];
si--;
down(1);
}
return 0;
}
AcWing 839. 模拟堆
堆的下表要从1开始 hp[++si] = val
方便 down() 和 up() 操作
#include
using namespace std;
const int N = 100010;
int h[N],si; // 堆的结构是一维数组,si表示下标,也代表堆的大小
int hp[N],ph[N],idx; // ph表示idx对应堆的下标, hp表示下标对应idx(第几个插入的)
void myswap(int t,int u){
swap(ph[hp[t]],ph[hp[u]]);
swap(hp[t],hp[u]);
swap(h[t],h[u]);
}
void down(int u){
int t = u; // t表示 三者最小的下标 , 一定要是t啊
if(u*2 <= si && h[u*2] < h[t]) t = u*2;
if(u*2+1 <= si && h[u*2+1] < h[t]) t = u*2+1;
if(t != u){
myswap(t,u);
down(t);
}
}
void up(int u){
if(u/2 >= 1 && h[u/2] > h[u]){
myswap(u,u/2);
up(u/2);
}
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
while(n--){
string op; cin >> op;
if(op == "I"){
int x; cin >> x;
si++;
idx++;
ph[idx] = si;
hp[si] = idx;
h[si] = x;
up(si);
}else if(op == "PM"){
cout << h[1] << endl;
}else if(op == "DM"){
myswap(1,si);
si--;
down(1);
}else if(op == "D"){
int k; cin >> k;
k = ph[k]; // 必须保存当前别删除节点的位置
myswap(si,k);
si--;
up(k); // down 和 up的操作不能缺少
down(k);
}else{
int k,x; cin >> k >> x;
h[ph[k]] = x;
down(ph[k]);
up(ph[k]);
}
}
return 0;
}
AcWing 840. 模拟散列表
mod 取 质数
for(int i = 100000; ; i++){
bool flag = true;
for(int j = 2; j*j <= i; j++){
if(i % j == 0){
flag = false;
break;
}
}
if(flag){
cout << i <<endl;
break;
}
}
拉链法,邻接表
#include
#include
using namespace std;
const int N = 100003; // 最小的质数
int h[N],e[N],ne[N],idx; // h[N] 和 ne[N] 存的都是下标, -1表示空指针
int n;
void insert(int x){
int k = ((x % N) + N) % N;
e[idx] = x; // 头插法
ne[idx] = h[k];
h[k] = idx;
idx++;
}
bool find(int x){
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 main(){
ios::sync_with_stdio(false); cin.tie(0);
memset(h,-1,sizeof(h)); //槽都指向空节点 -1
cin >> n;
while(n--){
char op; int x;
cin >> op >> x;
if(op == 'I') insert(x);
else{
if(find(x)) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
#include
#include
using namespace std;
const int N = 200003; // 大小是2-3倍,且是质数
int h[N],null = 0x3f3f3f3f;
int find(int x){
int k = ((x%N)+N)%N;
while(h[k] != null && h[k] != x){
k++;
if(k == N) k = 0; // 往后面找完了,就从开头开始找
}
return k;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
memset(h,null,sizeof(h)); //一定要初始化,没有使用过
int n; cin >> n;
while(n--){
char op; int x; cin >> op >> x;
int k = find(x);
if(op == 'I') h[k] = x;
else{
if(h[k] == x) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
AcWing 841. 字符串哈希
#include
using namespace std;
const int N = 100010;
typedef unsigned long long ULL;
int n,m;
int P = 131;
ULL h[N],p[N]; // h[N] 存放字符串的hash值,p[N] 存放权
int get(int l,int r){
return h[r] - h[l-1]*p[r-l+1];
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
char s[N]; cin >> s+1;
p[0] = 1; // 初始化
for(int i = 1; i <= n; i++){
p[i] = p[i-1] * P; // 权p[1] = P
h[i] = h[i-1] * P + s[i]; // h[1] = s[1] , h[2] = h[1]*p + s[2]
}
while(m--){
int l1,r1,l2,r2; cin >> l1 >> r1 >> l2 >> r2;
if(get(l1,r1) == get(l2,r2)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}