bzoj1507 [NOI2003]Editor(块状链表)
2018-01-17 18:38
411 查看
Description
Input
输入文件editor.in的第一行是指令条数t,以下是需要执行的t个操作。其中: 为了使输入文件便于阅读,Insert操作的字符串中可能会插入一些回车符,请忽略掉它们(如果难以理解这句话,可以参考样例)。 除了回车符之外,输入文件的所有字符的ASCII码都在闭区间[32, 126]内。且行尾没有空格。 这里我们有如下假定: MOVE操作不超过50000个,INSERT和DELETE操作的总个数不超过4000,PREV和NEXT操作的总个数不超过200000。所有INSERT插入的字符数之和不超过2M(1M=1024*1024),正确的输出文件长度不超过3M字节。 DELETE操作和GET操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT操作必然不会试图把光标移动到非法位置。 输入文件没有错误。 对C++选手的提示:经测试,最大的测试数据使用fstream进行输入有可能会比使用stdio慢约1秒。
Output
输出文件editor.out的每行依次对应输入文件中每条GET指令的输出。Sample Input
15Insert 26
abcdefghijklmnop
qrstuv wxy
Move 15
Delete 11
Move 5
Insert 1
^
Next
Insert 1
_
Next
Next
Insert 4
.\/.
Get 4
Prev
Insert 1
^
Move 0
Get 22
Sample Output
.\/.abcde^_^f.\/.ghijklmno
[Submit][Status][Discuss]
分析:
块状链表的代码实现
块状链表的思路很好理解,代码实现还是比较好理解的
初始化
首先我们需要初始化因为链表的特点就是空间上的不连续性,但是c++的链表非常的 * *,因此在这里我们选择用数组模拟链表
所以我们还是在代码中开够所有的结点
设置一个队列q,相当于内存池,储存的是没有使用过的链表结点标号
void init() { for (int i=1;i<=blocknum;i++) q.push(i); a[0].len=0; a[0].nxt=-1; }
输入
读入的时候需要注意(样例就可以很好的说明)插入的字符串有可能包含space,但是不包括“\n”,我们就不能用scanf
因此我们编写专门的函数,用getchar读入
void read(int len) //需要特殊的读入函数 因为字符串中可能有空格 { int i=0; while (i<len) { char c=getchar(); s[i]=c; i++; if (c<32||c>126) i--; //防止读入不合法字符 } }
申请节点
申请新节点int newnode() { int t=q.front(); q.pop(); return t; }
删除结点
因为我们需要节约空间,因此删除的结点可以重新扔到内存池中,以备往后使用void delnode(int t) { q.push(t); }
定位
我们用链表的检索方式,直接沿着链暴力检索,最后定位到第now个块的第pos位void find(int &pos,int &now) //从第一个块开始搜索,搜索位置pos所属的块的编号 { for (now=0;a[now].nxt!=-1&&pos>a[now].len;now=a[now].nxt) pos-=a[now].len; }
fillnode
pos—>结点编号,n—>结点size,data[]—>字符数组,nxt—>下一块memcpy(数组1的起始指针,数组2的起始指针,size)
效果:数组2 起始指针开始的size位 复制到了数组1中
void fillnode(int pos,int n,char data[],int nxt) { a[pos].nxt=nxt; a[pos].len=n; memcpy(a[pos].data,data,n); //将data中前n个元素复制到块pos中 }
分裂
我们直接申请一个新节点,把当前结点的后半部分 copy到新节点中
不要忘记维护nxt
void divide(int pos,int p) { if (a[pos].len==p) return; int t=newnode(); fillnode(t,a[pos].len-p,a[pos].data+p,a[pos].nxt); a[pos].nxt=t; a[pos].len=p; }
合并
为了保证数据结构的时间复杂度我们需要即使合并小的block:
从pos开始一条链检索下去,相邻两块block只要不超过blocksize就进行合并
void maintain(int pos) //当前块与后一个块合并 { int t; for (;pos!=-1;pos=a[pos].nxt) for (t=a[pos].nxt;t!=-1&&a[pos].len+a[t].len<blocksize;t=a[t].nxt) { memcpy(a[pos].data+a[pos].len,a[t].data,a[t].len); a[pos].len+=a[t].len; a[pos].nxt=a[t].nxt; delnode(t); } }
插入
我们定位插入位置:第now块的第pos位把第now块从pos分成两块,前半部分的标号就是now
我们把新字符分割成数块block,直接线性插入
因为插入了结点,块的大小会发生改变,因此我们需要合并一下小的block
void insert(int pos,int n) { int now,i,t; //now记录当前位置所在的块的编号 find(pos,now); divide(now,pos); for (i=0;i+blocksize<=n;i+=blocksize) { t=newnode(); fillnode(t,blocksize,s+i,a[now].nxt); a[now].nxt=t; now=t; } if (i<n) //剩下的元素不足以填满一个块,需要特殊处理一下 { t=newnode(); fillnode(t,n-i,s+i,a[now].nxt); a[now].nxt=t; } maintain(now); }
删除
我们还是首先定位删除的位置:第now块的第pos位通过拆块的方式使当前块中要删除的元素单独分到一个块中
最后如果剩下的不够一个block
我们还是把待删除的数据分到一个块内删除
因为删除了结点,块的大小会发生改变,因此我们需要合并一下小的block
void del(int pos,int n) { int i,now,t; find(pos,now); divide(now,pos); //删除的时候先处于第一个位置所在的块,通过拆块的方式使当前块中要删除的元素单独分到一个块中 for (i=a[now].nxt;i!=-1&&n>a[i].len;i=a[i].nxt) n-=a[i].len; divide(i,n); i=a[i].nxt; for (t=a[now].nxt;t!=i;t=a[now].nxt) a[now].nxt=a[t].nxt,delnode(t); maintain(now); }
询问
询问的思路和插入删除差不多先定位查询位置
把查询的区间分成单独的block
复制到一个字符数组中,直接输出字符串即可
void get(int pos,int n) { int i,now,t; find(pos,now); i=min(n,a[now].len-pos); memcpy(s,a[now].data+pos,i); for (t=a[now].nxt;t!=-1&&i+a[t].len<=n;t=a[t].nxt) { memcpy(s+i,a[t].data,a[t].len); i+=a[t].len; } if (i<n&&t!=-1) memcpy(s+i,a[t].data,n-i); s =0; //防止出错结尾清零 }
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1<<25;
const int blocksize=20000;
const int blocknum=N/blocksize*3;
int n,cur;
char s[N+10];
queue<int> q;
struct node{
char data[blocksize];
int len,nxt; //记录块中的元素个数 下一个块的编号(模拟链表)
};
node a[blocknum+100];
int newnode() { int t=q.front(); q.pop(); return t; }
void delnode(int t) { q.push(t); }
void find(int &pos,int &now) //从第一个块开始搜索,搜索位置pos所属的块的编号
{
for (now=0;a[now].nxt!=-1&&pos>a[now].len;now=a[now].nxt) pos-=a[now].len;
}
void fillnode(int pos,int n,char data[],int nxt)
{
a[pos].nxt=nxt; a[pos].len=n;
memcpy(a[pos].data,data,n); //将data中前n个元素复制到块pos中
}
void divide(int pos,int p) { if (a[pos].len==p) return; int t=newnode(); fillnode(t,a[pos].len-p,a[pos].data+p,a[pos].nxt); a[pos].nxt=t; a[pos].len=p; }
void maintain(int pos) //当前块与后一个块合并 { int t; for (;pos!=-1;pos=a[pos].nxt) for (t=a[pos].nxt;t!=-1&&a[pos].len+a[t].len<blocksize;t=a[t].nxt) { memcpy(a[pos].data+a[pos].len,a[t].data,a[t].len); a[pos].len+=a[t].len; a[pos].nxt=a[t].nxt; delnode(t); } }
void insert(int pos,int n) { int now,i,t; //now记录当前位置所在的块的编号 find(pos,now); divide(now,pos); for (i=0;i+blocksize<=n;i+=blocksize) { t=newnode(); fillnode(t,blocksize,s+i,a[now].nxt); a[now].nxt=t; now=t; } if (i<n) //剩下的元素不足以填满一个块,需要特殊处理一下 { t=newnode(); fillnode(t,n-i,s+i,a[now].nxt); a[now].nxt=t; } maintain(now); }
void del(int pos,int n) { int i,now,t; find(pos,now); divide(now,pos); //删除的时候先处于第一个位置所在的块,通过拆块的方式使当前块中要删除的元素单独分到一个块中 for (i=a[now].nxt;i!=-1&&n>a[i].len;i=a[i].nxt) n-=a[i].len; divide(i,n); i=a[i].nxt; for (t=a[now].nxt;t!=i;t=a[now].nxt) a[now].nxt=a[t].nxt,delnode(t); maintain(now); }
void get(int pos,int n)
{
int i,now,t;
find(pos,now);
i=min(n,a[now].len-pos);
memcpy(s,a[now].data+pos,i);
for (t=a[now].nxt;t!=-1&&i+a[t].len<=n;t=a[t].nxt)
{
memcpy(s+i,a[t].data,a[t].len);
i+=a[t].len;
}
if (i<n&&t!=-1) memcpy(s+i,a[t].data,n-i);
s
=0;
}
void init() { for (int i=1;i<=blocknum;i++) q.push(i); a[0].len=0; a[0].nxt=-1; }
void read(int len) //需要特殊的读入函数 因为字符串中可能有空格
{
int i=0;
while (i<len)
{
char c=getchar();
s[i]=c;
i++;
if (c<32||c>126) i--; //
}
}
int main()
{
init();
scanf("%d",&n);
char opt[20];
int pos;
//所谓的光标实际上就是一个数组的下标
for (int i=1;i<=n;i++)
{
scanf("%s",opt);
if (opt[0]=='I')
{
scanf("%d",&pos);
read(pos);
insert(cur,pos);
}
else if (opt[0]=='M') scanf("%d",&cur); //直接移动光标
else if (opt[0]=='D')
{
scanf("%d",&pos);
del(cur,pos);
}
else if (opt[0]=='G')
{
scanf("%d",&pos);
get(cur,pos);
puts(s);
}
else if (opt[0]=='P') cur--; //光标前移
else if (opt[0]=='N') cur++; //光标后移
}
}
相关文章推荐
- BZOJ 1507([NOI2003]Editor-块状链表)
- bzoj 1507: [NOI2003]Editor (块状链表)
- [BZOJ1507][NOI2003]Editor(块状链表)
- 1507: [NOI2003]Editor(块状链表)
- 【BZOJ】1507: [NOI2003]Editor(Splay)
- bzoj1507 [NOI2003]Editor
- bzoj1507: [NOI2003]Editor
- 【BZOJ-1507】Editor 块状链表
- 块状链表(附NOI 2003 Editor,POJ 2131 Key Insertion)
- [BZOJ1507][NOI2003]Editor
- BZOJ 1507 [NOI2003]Editor
- BZOJ1507 [NOI2003]Editor
- BZOJ 1507 [NOI2003]Editor
- bzoj 1507: [NOI2003]Editor
- 【BZOJ 1507】[NOI2003]Editor
- 【bzoj1507】[NOI2003]Editor /【bzoj1269】[AHOI2006]文本编辑器editor Splay
- 【BZOJ1507】[NOI2003]Editor Splay
- 洛谷.4008.[NOI2003]editor文本编辑器(块状链表)
- 【bzoj1507】[NOI2003]Editor
- BZOJ 1507: [NOI2003]Editor rope