cogs 330. [NOI2003] 文本编辑器
★★★ 输入文件:editor2003.in
输出文件:editor2003.out
简单对比
时间限制:2 s 内存限制:128 MB
【问题描述】
很久很久以前,DOS3.x的程序员们开始对EDLIN感到厌倦。于是,人们开始纷纷改用自己写的文本编辑器……
多年之后,出于偶然的机会,小明找到了当时的一个编辑软件。进行了一些简单的测试后,小明惊奇地发现:那个软件每秒能够进行上万次编辑操作(当然,你不能手工进行这样的测试)!于是,小明废寝忘食地想做一个同样的东西出来。你能帮助他吗? 为了明确任务目标,小明对“文本编辑器”做了一个抽象的定义:
文本:由0个或多个字符构成的序列。这些字符的ASCII码在闭区间[32, 126]内,也就是说,这些字符均为可见字符或空格。
光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。
文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下六条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。
操作名称 |
输入文件中的格式 |
功能 |
MOVE(k) |
Move k |
将光标移动到第k个字符之后,如果k=0,将光标移到文本第一个字符之前 |
INSERT(n, s) |
Insert n↵ S |
在光标后插入长度为n的字符串s,光标位置不变,n ≥ 1 |
DELETE(n) |
Delete n |
删除光标后的n个字符,光标位置不变,n ≥ 1 |
GET(n) |
Get n |
输出光标后的n个字符,光标位置不变,n ≥ 1 |
PREV() |
Prev |
光标前移一个字符 |
NEXT() |
Next |
光标后移一个字符 |
比如从一个空的文本编辑器开始,依次执行操作INSERT(13, “Balanced□tree”),MOVE(2),DELETE(5),NEXT(),INSERT(7, “□editor”),MOVE(0),GET(15)后,会输出“Bad□editor□tree”,如下表所示:
操作 |
操作前的文本 |
操作后的结果 |
INSERT(13, "Balanced□tree") |
| (只有光标,文本为空) |
|Balanced□tree |
MOVE(2) |
|Balanced□tree |
Ba|lanced□tree |
DELETE(5) |
Ba|lanced□tree |
Ba|d□tree |
NEXT() |
Ba|d□tree |
Bad|□tree |
INSERT(7, "□editor") |
Bad|□tree |
Bad|□editor□tree |
MOVE(0) |
Bad|□editor□tree |
|Bad□editor□tree |
GET(15) |
|Bad□editor□tree |
输出“Bad□editor□tree” |
上表中,“|”表示光标,“□”表示空格。 你的任务是:
- 建立一个空的文本编辑器。
- 从输入文件中读入一些操作指令并执行。
- 对所有执行过的GET操作,将指定的内容写入输出文件。
【输入文件】
输入文件的第一行是指令条数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秒,因此建议在可以的情况下使用后者。
【输出文件】
输出文件的每行依次对应输入文件中每条GET指令的输出。
【样例输入】
15
Insert 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
【样例输出】
.\/.
abcde^_^f.\/.ghijklmno
题解:
本题用splay来维护,我们先在splay中插入两个'*'表示在序列的开端和结尾各有一个'*'方便以后处理,pos记录当前光标所在位置,因为在序列开头加上了'*',所以pos=光标所在的位置+1。
对于插入操作,可以先把要插入的序列建成一棵splay,假设光标在第k个字符的后面,那么让第k-1个字符旋至根,第k个字符旋至根的右孩子,所要加入的区间就是根的右孩子的左边那一块,直接把刚建好的splay插入。
删除操作就是找到要删除区间的左端点,把它旋至根,找到要删除区间的右端点,把它旋至根的右孩子,要删除的区间就是根的右孩子的左边那一块,直接删除即可。
剩下的操作就是直接对pos的操作,很好理解。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 using namespace std; 10 typedef long long LL; 11 const int maxn=3000000; 12 int root,tot,fa[maxn],lc[maxn],rc[maxn],siz[maxn]; 13 int T,N,pos=1; 14 char opt[20],ch[maxn],key[maxn]; 15 inline void update(int x){ 16 siz[x]=siz[lc[x]]+1+siz[rc[x]]; 17 } 18 inline void r_rotate(int x){ 19 int y=fa[x]; 20 lc[y]=rc[x]; 21 if(rc[x]) fa[rc[x]]=y; 22 fa[x]=fa[y]; 23 if(y==lc[fa[y]]) lc[fa[y]]=x; 24 else rc[fa[y]]=x; 25 fa[y]=x; rc[x]=y; 26 update(y); update(x); 27 } 28 inline void l_rotate(int x){ 29 int y=fa[x]; 30 rc[y]=lc[x]; 31 if(lc[x]) fa[lc[x]]=y; 32 fa[x]=fa[y]; 33 if(y==lc[fa[y]]) lc[fa[y]]=x; 34 else rc[fa[y]]=x; 35 fa[y]=x; lc[x]=y; 36 update(y); update(x); 37 } 38 inline void splay(int x,int s){ 39 int p; 40 while(fa[x]!=s){ 41 p=fa[x]; 42 if(fa[p]==s){ 43 if(x==lc[p]) r_rotate(x); 44 else l_rotate(x); 45 break; 46 } 47 else if(x==lc[p]){ 48 if(p==lc[fa[p]]) r_rotate(x),r_rotate(x); 49 else r_rotate(x),l_rotate(x); 50 } 51 else if(x==rc[p]){ 52 if(p==rc[fa[p]]) l_rotate(x),l_rotate(x); 53 else l_rotate(x),r_rotate(x); 54 } 55 } 56 if(s==0) root=x; 57 } 58 inline int find(int x,int rank){ 59 if(siz[lc[x]]+1==rank) return x; 60 else if(siz[lc[x]]+1>rank) return find(lc[x],rank); 61 else return find(rc[x],rank-siz[lc[x]]-1); 62 } 63 inline void split(int l,int r){ 64 int x=find(root,l-1),y=find(root,r+1); 65 splay(x,0); splay(y,root); 66 } 67 inline void newnode(int &x,char ch,int fath){ 68 x=++tot; 69 fa[x]=fath; key[x]=ch; 70 lc[x]=rc[x]=0; siz[x]=1; 71 } 72 inline void insert(int &x,int l,int r,int fath){ 73 int mid=(l+r)>>1; 74 newnode(x,ch[mid],fath); 75 if(l+1<=mid) insert(lc[x],l,mid-1,x); 76 if(mid+1<=r) insert(rc[x],mid+1,r,x); 77 update(x); 78 } 79 inline void Delete(){ 80 split(pos+1,pos+N); 81 fa[lc[rc[root]]]=0; lc[rc[root]]=0; 82 update(rc[root]); update(root); 83 } 84 inline void print(int x){ 85 if(lc[x]) print(lc[x]); 86 printf("%c",key[x]); 87 if(rc[x]) print(rc[x]); 88 } 89 inline void Get(){ 90 split(pos+1,pos+N); 91 print(lc[rc[root]]); 92 } 93 int main(){ 94 newnode(root,'*',0); newnode(rc[root],'*',root); update(root); 95 scanf("%d",&T); 96 while(T--){ 97 scanf("%s",opt); 98 switch(opt[0]){ 99 case'M': 100 scanf("%d",&N); 101 pos=N+1; 102 break; 103 case'I': 104 scanf("%d",&N); 105 for(int i=1;i<=N;i++){ 106 ch[i]=getchar(); 107 while(ch[i]<32||ch[i]>126) ch[i]=getchar(); 108 } 109 split(pos+1,pos);//分割光标和光标的下一位 110 insert(lc[rc[root]],1,N,rc[root]); 111 update(rc[root]); update(root); 112 break; 113 case'D': 114 scanf("%d",&N); 115 Delete(); 116 break; 117 case'G': 118 scanf("%d",&N); 119 Get(); printf("\n"); 120 break; 121 case'P': 122 pos--; break; 123 case'N': 124 pos++; break; 125 } 126 } 127 return 0; 128 }