[BZOJ1507][NOI2003]Editor(块状链表)

26 篇文章 0 订阅
2 篇文章 0 订阅

题目描述

传送门

题解

块状链表模板题,主要能实现几个简单的操作:添加一段区间,删除一段区间,输出一段区间内的元素。
操作的主要过程如下:
插入:找到光标所在的块以及光标在块中的位置,将这个块从这个位置分成两半。将需要插入的区间分成若干个块插入在中间。将碎块合并。
删除:分别找到删除的区间的左右端点所在的块和它们在块中的位置,将这两个块从各自的位置分成两半。将中间的块删除。将碎块合并。
输出:找到光标所在的块和它在块中的位置,向后将一段一段的块拼成给定长度的块。

代码

// BZOJ 1507
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

const int N=1<<25;
const int blocksize=20000;
const int blocknum=N/blocksize*3;

int T,_;
int cur;
char str[5000005],opt[100];
queue <int> q;
struct node
{
    char data[blocksize+111];
    int len,nxt;
}a[blocknum+1000];

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=-1;
    while (i<len-1)
    {
        i++;
        char c=getchar();
        str[i]=c;
        if (c<32||c>126) i--;
    }
}
//新开一个块的节点 
int newnode()
{
    int temp=q.front(); q.pop();
    return temp;
}
//回收块的节点 
void delnode(int t)
{
    q.push(t);
}
//找到pos所在的块,并使pos表示在当前块中的位置 
void find(int &pos,int &now)
{
    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);
}
//将块pos在p位置前后分开,变成两个块 
void split(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);
        }
}
//在光标pos处插入长度为n的str 
void insert(int pos,int n)
{
    int now,i,t;
    //now表示光标所在的块,pos表示光标在这个块中的位置 
    find(pos,now);
    split(now,pos);
    for (i=0;i+blocksize<=n;i+=blocksize)
    {
        t=newnode();
        fillnode(t,blocksize,str+i,a[now].nxt);
        a[now].nxt=t;
        now=t;
    }
    if (i<n)
    {
        t=newnode();
        fillnode(t,n-i,str+i,a[now].nxt);
        a[now].nxt=t;
    }
    maintain(now);
}
//从光标pos开始删除长度为n的字符串 
void del(int pos,int n)
{
    int i,now,t;
    //now表示光标所在的块,pos表示光标在这个块中的位置 
    find(pos,now);
    split(now,pos);
    //找到删除的末尾的点所处的块 
    for (i=a[now].nxt;i!=-1&&n>a[i].len;i=a[i].nxt)
        n-=a[i].len;
    split(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);
}
//从pos这个位置开始输出长度为n的字符串 
void get(int pos,int n)
{
    int i,now,t;
    find(pos,now);
    i=min(n,a[now].len-pos);
    memcpy(str,a[now].data+pos,i);
    for (t=a[now].nxt;t!=-1&&i+a[t].len<=n;t=a[t].nxt)
    {
        memcpy(str+i,a[t].data,a[t].len);
        i+=a[t].len;
    }
    if (i<n&&t!=-1) memcpy(str+i,a[t].data,n-i);
    str[n]=0;
}
int main()
{
    init();
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",opt);
        if (opt[0]=='M') scanf("%d",&cur);//改变光标的位置 
        if (opt[0]=='I')//插入一段区间 
        {
            scanf("%d",&_);
            read(_);
            insert(cur,_);
        }
        if (opt[0]=='P') cur--;//光标左移 
        if (opt[0]=='N') cur++;//光标右移 
        if (opt[0]=='D')//删除一段区间 
        {
            scanf("%d",&_);
            del(cur,_);
        }
        if (opt[0]=='G')//输出一段区间 
        {
            scanf("%d",&_);
            get(cur,_);
            puts(str);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值