初识 块状数组

51 篇文章 1 订阅
24 篇文章 0 订阅

问题

给出一个长串,然后给出n个操作,操作有两种,在某个位置插入一个字符,或者查询第x个位置上的字符是什么

对于这个问题,我们有两种解决方案。

1:数组

查询 O(1) 但是插入最坏可以达到 O(len)

2:链表

插入 O(1) 但是查询最坏也可以达到O(len)

所以如何优化这个问题。

这里介绍一种数据结构——块状数组

何为块状数组?

普通的数组,每个节点都是连续的。

而块状数组是把一个大块分为若干个小块,然后连接起来,因此得名块状数组。

数组块数*每个块的最大长度>=总的元素+操作个数,即分块以后,每个块都有个增大的空间(让其大于操作个数即可)

分块,就是优美的暴力~

如何查询与插入?

sum[i]为第一块到第i块字符的总个数。

再插入时我们可以利用二分快速寻找位置所对应的块号。

而在查询时,也是利用二分来查询块号输出。

在最坏情况下=sqrt(str)+n;

在平均情况下=sqrt(str+n);

block_num,块数:

准确讲,=(str+n)/block_len

结合平均情况来讲,block_num=block_len=sqrt(str+n)、

代码没用结构体封装,因为我不喜欢。

初级块状数组,代码较短,仅支持插入与查询操作。

题目链接

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define il inline
using namespace std;
const int maxm=2100;
int block_len,block_num,n;//块长以及块数
int sum[maxm];//从1块到当前块的总字符个数
char str[1100000];
struct node{
	int size;
	char date[maxm<<1];
}block[maxm];
il void get_sum()//统计
{
	sum[1]=block[1].size;  
    for(int i=2;i<=block_num;i++)  
     sum[i]=sum[i-1]+block[i].size;  
} 
il void push_in(int id,char s){block[id].date[++block[id].size]=s;}//初始插入
il void init()
{
	int str_len=strlen(str);
	block_len=block_num=sqrt((double)str_len+(double)n);
	for(int i=1;i<=block_num;i++)
	 block[i].size=0,sum[i]=0;
	for(int i=0;i<str_len;i++)
	 push_in(i/block_len+1,str[i]);
	get_sum();
}
il void insert(int id,int pos,char s)
{
	if(pos>block[id].size) 
     push_in(id,s);
    else
    {
    	for(int i=++block[id].size;i>pos;i--)
    	 block[id].date[i]=block[id].date[i-1];//移动插入
    	block[id].date[pos]=s;
    }
    get_sum();
}
il char ask(int id,int pos){return block[id].date[pos];}
int main()
{
	scanf("%s",str);
	scanf("%d",&n);
	init();
	char qus[3],ss[3];
	for(int i=1,pos;i<=n;i++)
	{
		scanf("%s",qus);
		if(qus[0]=='Q')
		{
			scanf("%d",&pos);
			int block_id=lower_bound(sum+1,sum+block_num+1,pos)-sum;
			int block_pos=pos-sum[block_id-1];//不要忘记-1
			printf("%c\n",ask(block_id,block_pos));
		}
		else
		{
			scanf("%s%d",ss,&pos);
			int block_id=lower_bound(sum+1,sum+block_num+1,pos)-sum;
			int block_pos=pos-sum[block_id-1];
			insert(block_id,block_pos,ss[0]);
		}
	}
	
	return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值