先推广一下

求赞

我们考虑这样的一个问题

给你一个序列,要求你支持插入,删除,查询单点值

如果用数组,查询O(1),插入删除最坏O(n)

如果用链表,插入删除O(1),查询最坏O(n)

如果用平衡树……

不要跟我说平衡树

那么我们是否可以考虑:将一个一个的数组以链表的形式串起来,这样是否会提高操作的效率,又是否会降低一些操作的效率呢?

可以手动模拟一下各种操作

块状链表就是这样一个略显暴力的算法

但其复杂度较为优秀,所以在很多地方的应用都非常广

用一句话说叫“弱弱联合”

码量稍大,但极易理解,打着打着就打出两百K行

先介绍一下比较基本的操作吧

Spilt

当一个块的长度过大,我们就可以考虑将其分裂成两个较小的块。

在处理类似于插入或者删除这类操作时,我们可以先从当前位置将其分裂成两个块,这样就可以十分方便的进行操作了。

Merge

同理,就是\(Split\)的逆运算。

部分Maintain

姑且称其为部分maintain吧,我也不知道叫什么。

在进行操作时,我们可能会使得一些块过大,一些块过小。

所以我们需要通过\(Spilt\)或者\(Merge\)来调整。

我们发现,在进行操作时所需要考虑的需要维护的块:区间前的那一块与区间开头块;区间末尾块与区间后的那一块。

这样做可能会使得块状链表没有在经过完整maintain操作时平衡,但会大大减少维护时的常数,而平衡程度也可以接受。

一般采用的维护方法:保证相邻两块大小加起来大于\(\sqrt{n}\),但每块大小不超过\(\sqrt{n}\),这样可以较好的维护平衡,同时不用考虑当块较大时的\(Split\)操作。

这是作者经过权衡后得出的做法,实测复杂度优秀,复杂度为\(O(1)\)。


然后,我们切入正题。

Insert

查找光标块内的位置,在此位置将块分裂,然后将字符串一块一块地插入

Delete

同理

Get

不需要分裂,直接利用\(memcpy\)函数,对其进行复制粘贴即可

代码中有较详细注释,贴代码

#include<bits/stdc++.h>
using namespace std;
char xch,xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
inline int read()
{
int x=0,f=1;char ch=getc();
while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}//为了使程序跑得更快所使用的读入优化
const int maxn=2e3+10;
struct node{
int nex,siz;//每一块数组的后继以及大小
char a[maxn<<1];
}b[maxn<<2];
int pool[maxn<<2],cnt,curpos;//内存池、指针以及当前光标位置
inline int modi(){return pool[cnt++];}//内存分配
inline void dele(int x){pool[--cnt]=x;}//内存回收
inline void init()
{
for(int i=1;i<(maxn<<2);++i) pool[i]=i;//维护内存池,动态分配回收内存
cnt=1;
b[0].siz=0,b[0].nex=-1;//新建一个0号节点,方便操作
}
inline void add(int x,int y,int num,char c[])//在第x块后添加一个编号为y的块,长度为num
{
if(y!=-1)
{
b[y].nex=b[x].nex,b[y].siz=num;
memcpy(b[y].a,c,num);
}
b[x].nex=y;
}
inline void merge(int x,int y)//将第x块和第y块合并
{
memcpy(b[x].a+b[x].siz,b[y].a,b[y].siz);
b[x].siz+=b[y].siz,b[x].nex=b[y].nex;
dele(y);
}
inline void split(int cur,int pos)//将第cur块从pos处分割
{
if(cur==-1||pos==b[cur].siz) return ;
add(cur,modi(),b[cur].siz-pos,b[cur].a+pos);
b[cur].siz=pos;
}
inline int pos(int &x)//寻找当前光标所在的块和块内位置
{
int now=0;
while(now!=-1&&x>b[now].siz) x-=b[now].siz,now=b[now].nex;
return now;
}
inline void insert(int p,int num,char c[])//在p位置之后插入长度为num的字符串
{
int now=pos(p);
split(now,p);
int tot=0,nb,st=now;
while(tot+maxn<=num)//维护块状链表平衡
{
nb=modi();
add(now,nb,maxn,c+tot);
tot+=maxn;
now=nb;
}
if(num-tot)
nb=modi(),add(now,nb,num-tot,c+tot);
if(b[now].siz+b[nb].siz<maxn&&nb!=-1)//不用对整个链表进行判断,部分maintain
merge(now,nb),nb=b[now].nex;
if(b[st].siz+b[b[st].nex].siz<maxn&&b[st].nex!=-1)//同理
merge(st,b[st].nex);
// maintain();
}
inline void erase(int p,int num)//在p位置之后删除长度为num的字符串
{
int now=pos(p);
split(now,p);
int nex=b[now].nex;
while(nex!=-1&&num>b[nex].siz)
num-=b[nex].siz,nex=b[nex].nex;
split(nex,num);
nex=b[nex].nex;
for(int i=b[now].nex;i!=nex;i=b[now].nex)
b[now].nex=b[i].nex,dele(i);
while(b[now].siz+b[nex].siz<maxn&&nex!=-1)//不用对整个链表进行判断,部分maintain
merge(now,nex),nex=b[now].nex;
// maintain();
}
char ans[20000000];
inline void get(int p,int num)//输出p位置后长度为num的字符串
{
int cur=pos(p);
int tot=b[cur].siz-p;
if(num<tot) tot=num;
memcpy(ans,b[cur].a+p,tot);
int now=b[cur].nex;
while(now!=-1&&num>=tot+b[now].siz)
{
memcpy(ans+tot,b[now].a,b[now].siz);
tot+=b[now].siz,now=b[now].nex;
}
if(num-tot>0&&now!=-1)
memcpy(ans+tot,b[now].a,num-tot);
ans[num]='\0';//为了不清空,用\0结束
printf("%s\n",ans);
}
inline char opt()
{
char c=getc();
while(c!='M'&&c!='I'&&c!='D'&&c!='G'&&c!='P'&&c!='N') c=getc();
return c;
}//为了不与读入优化冲突
int main()
{
// freopen("3.in","r",stdin);
// freopen("3.ans","w",stdout);
init();
int m; scanf("%d",&m);
for(int i=1;i<=m;++i)
{
switch(opt())
{
case 'M':curpos=read();break;
case 'I':
int tmp;
tmp=read();
for(int i=0;i<tmp;++i)
{
ans[i]=getc();
if(ans[i]<32||ans[i]>126) --i;
}
insert(curpos,tmp,ans);
break;
case 'D':
tmp=read();
erase(curpos,tmp);
break;
case 'G':
tmp=read();
get(curpos,tmp);
break;
case 'P':--curpos;break;
case 'N':++curpos;break;
}
}
return 0;
}

洛谷 P4008 [NOI2003]文本编辑器的更多相关文章

  1. 洛谷 P4008 [NOI2003]文本编辑器 解题报告

    P4008 [NOI2003]文本编辑器 题目描述 很久很久以前,\(DOS3.x\)的程序员们开始对 \(EDLIN\) 感到厌倦.于是,人们开始纷纷改用自己写的文本编辑器⋯⋯ 多年之后,出于偶然的 ...

  2. luogu P4008 [NOI2003]文本编辑器 splay 块状链表

    LINK:文本编辑器 这个东西感觉块状链表写细节挺多 (块状链表本来就难写 解释一下块状链表的做法:其实是一个个数组块 然后利用链表给链接起来 每个块的大小为sqrt(n). 这样插入删除的时候直接暴 ...

  3. P4008 [NOI2003]文本编辑器

    思路 FHQ Treap的板子 用FHQ Treap维护中序遍历序列即可 然后数组开够! 代码 #include <cstdio> #include <cstring> #in ...

  4. [NOI2003] 文本编辑器 (splay)

    复制炸格式了,就不贴题面了 [NOI2003] 文本编辑器 Solution 对于光标的移动,我们只要记录一下现在在哪里就可以了 Insert操作:手动维护中序遍历结果,即每次取中点像线段树一样一样递 ...

  5. [NOI2003]文本编辑器 [Fhq Treap]

    [NOI2003]文本编辑器 没啥好说的 就是个板子 #include <bits/stdc++.h> // #define int long long #define rep(a , b ...

  6. cogs 330. [NOI2003] 文本编辑器

    ★★★   输入文件:editor2003.in   输出文件:editor2003.out   简单对比 时间限制:2 s   内存限制:128 MB [问题描述] 很久很久以前,DOS3.x的程序 ...

  7. 【洛谷 P4008】 [NOI2003]文本编辑器 (Splay)

    题目链接 \(Splay\)先练到这吧(好像还有道毒瘤的维护数列诶,算了吧) 记录下光标的编号,维护就是\(Splay\)基操了. 另外数据有坑,数据是\(Windows\)下生成了,回车是'\n\r ...

  8. 洛谷 - P4008 - 文本编辑器 - 无旋Treap

    https://www.luogu.org/problem/P4008 无旋Treap也可以维护序列. 千万要注意要先判断p节点存在才进行Show操作,不然输出一个'\0'(或者RecBin里面的东西 ...

  9. 洛谷.4008.[NOI2003]editor文本编辑器(块状链表)

    题目链接 st(n)表示sqrt(n) 为使块状链表不会退化,通常将每块的大小S维持在[st(n)/2,2st(n)]中,这样块数C也一定[st(n)/2,2st(n)]中 在此使用另一种方法(方便) ...

随机推荐

  1. Linxu 修改主机名

    方法一: # hostname NEW_NAME <这种方法只对当前系统有效,重启后无效> 方法二: # hostnamectl set-hostname NEW_NAME:设定主机名,永 ...

  2. 使用 JavaScript 将 HTML 转换为 PDF

    使用 JavaScript 将 HTML 转换为 PDF 更多相关学习资料参见http://www.pdfdownload.cn/b/ba_index.php 在本文中,我们将了解如何在浏览器(即完全 ...

  3. Python+Selenium自动化-定位一组元素,单选框、复选框的选中方法

    Python+Selenium自动化-定位一组元素,单选框.复选框的选中方法   之前学习了8种定位单个元素的方法,同时webdriver还提供了8种定位一组元素的方法.唯一区别就是在单词elemen ...

  4. BTC芯片介绍

    BTC芯片介绍 Innosilicon宣布全球第一和最佳的28nm比特币ASIC和参考矿机 A1Craft(也称为A1)是2013年世界上最好的BTC ASIC,这是比特币区块哈希算法的易于使用,定制 ...

  5. 台积电5nm光刻技术

    台积电5nm光刻技术 在IEEE IEDM会议上,台积电发表了一篇论文,概述了其5nm工艺的初步成果.对于目前使用N7或N7P工艺的客户来说,下一步将会采用此工艺,因为这两种工艺共享了一些设计规则.新 ...

  6. CUDA C++编程接口:编译

    CUDA C++编程接口:编译 一.概述 CUDA C++为熟悉C++编程语言的用户提供了一个简单的路径,以方便地编写程序以执行该设备. 它由一组最小的扩展到C++语言和运行库. 在编程模型中引入了核 ...

  7. Idea快捷键大全(Windows)

    Ctrl 快捷键 介绍 Ctrl + F 在当前文件进行文本查找 (必备) Ctrl + R 在当前文件进行文本替换 (必备) Ctrl + Z 撤销 (必备) Ctrl + Y 删除光标所在行 或 ...

  8. java面试必知必会——排序

    二.排序 时间复杂度分析 排序算法 平均时间复杂度 最好 最坏 空间复杂度 稳定性 冒泡 O(n²) O(n) O(n²) O(1) 稳定 选择 O(n²) O(n²) O(n²) O(1) 不稳定 ...

  9. 新增秒杀功能、优惠券、支付宝、Docker,newbee-mall升级版开源啦!

    最近是非常非常非常忙,一方面是公司的事情比较多,另外⼀点是最近在准备诉讼材料.⾄于诉讼的是谁,⼤家可以去看我之前写的几篇文章,所以本来这周是不打算更新文章的.不过,昨天慕课网的法务联系我的律师了,终于 ...

  10. 视图:dba_hist_wr_control查询到两套库的awr保留策略

    问题描述:有一个问题,有同事在查询awr报告收集策略的时候,发现有两个库的策略,一套自己的,另一套已经找不到属于谁了,那么究竟是什么情景会出现这样的场景呢? 1.一开始网上找解答也没有得到解决,询问技 ...