1. 程式人生 > >【POJ 2887】Big String(塊狀陣列)

【POJ 2887】Big String(塊狀陣列)

Time Limit: 1000MS Memory Limit: 131072K
Total Submissions: 6380 Accepted: 1537

Description

You are given a string and supposed to do some string manipulations.

Input

The first line of the input contains the initial string. You can assume that it is non-empty and its length does not exceed 1,000,000.

The second line contains the number of manipulation commands N

(0 < N 2,000). The following N lines describe a command each. The commands are in one of the two formats below:

  1. I ch p: Insert a character ch before the p-th character of the current string. If p is larger than the length of the string, the character is appended to the end of the string.
  2. Q p: Query the p
    -th character of the current string. The input ensures that the p-th character exists.

All characters in the input are digits or lowercase letters of the English alphabet.

Output

For each Q command output one line containing only the single character queried.

Sample Input

ab
7
Q 1
I c 2
I d 4
I e 2
Q 5
I f 1
Q 3

Sample Output

a
d
e

Source


學到個新東西,挺實用也挺有趣。

題目大意是給出一個初始字串s 之後有q次操作

每次操作有兩種

Q x(0 < x <= len(s)) 表示查詢當前串中第x個字元

I c x(c為字母 0 < x <= len(s)+1)表示在第x個位置插入c字元 x == len+1表示在串尾插入

如果只有查詢這一種操作就簡單的很,但還有插入操作,如果O(n)的遍歷 妥妥的會超時 這裡就要用到一個新的知識點——塊狀連結串列(陣列)

用法就是把字串分割成一個個塊 此題每個塊需要大小為1000 否則會超時

劃分的方式就是把字串先處理成連結串列 每個節點存當前位置的字元 每存1000個字元 就把當前位置記錄一下 這樣就相當於把字串分割成了一個個長為1000的小塊(最後一塊長度可能不足1000)

最後得到的其實是需多個指標 指向每一個“關節” 或者說是分割點

這樣對於每次查詢 可以先找出該字元所處的塊 然後由塊的端點(分割點)暴力找這個字元即可 這樣就可以用空間換時間 而且物超所值(每次查詢O(1000))

插入略微麻煩一點 跟查詢同樣的方法 找到插入的位置,然後用連結串列的插入 把新字元生成的節點插入到當前位置 如果這樣就結束了 你會發現插入點之後的相對位置就亂了

或者說之後的分割點指向的位置就不對了 當前插入的塊中的字元數量會變為1001(此處只考慮插入塊在中間 暫時不考慮尾塊) 那麼就要處理掉這個1

方法就是讓1往後移 一直移動到最後一個塊 由於最後一個塊不足1000 所以加一位不會受到影響

實現方法就是把當前塊往後的所有分割點往前指一個字元 可以自行出一組資料演試一下 1000比較大 可以讓每個塊大小為3 然後自行實現一下 理解後會發現這種做法真的很美妙

剛才連結串列的後面我寫了個(陣列) 因為連結串列略耗時(我用的C++裡的new 可能malloc不會出現超時) 所以用了陣列和前向星 節省了一半多的時間=.=

這樣這題也就解出來了 程式碼如下(我這裡用的前指標 就是由表尾指向表頭 我感覺這樣寫塊狀連結串列比較舒服):

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread() freopen("in.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const double eps = 1e-8;
const int per = 1000;

struct Node
{
	char ch;
	//前指標
	int pre;
};

//資料儲存
Node nd[2333333];
//分割點指標   連結串列頭  連結串列尾
int iter[1010],head,end;
//讀取字串
char str[1000100];
//實時串長   前向星指標
int len,tp;

//建立新節點 返回下標
int NewNode(char ch)
{
	nd[tp].ch = ch;
	return tp++;
}

//查詢串中某位置字元
char Search(int pos)
{
	//找到該字元所在的塊
	int key = (pos+per-1)/per;
	int tmp;

	//如果是尾塊 分割點指標為空 直接從表尾開始找
	if(key*per > len)
	{
		tmp = end;
		for(int i = pos; i < len; ++i) tmp = nd[tmp].pre;
		return nd[tmp].ch;
	}

	//否則從所在塊的分割點往前推進找到該字元
	tmp = iter[key];
	pos %= per;
	if(pos) pos = per-pos;
	while(pos--) tmp = nd[tmp].pre;
	return nd[tmp].ch;
}

//在pos處插入字元ch
void Add(int pos,char ch)
{
	//找到該字元所處塊
	int key = (pos+per-1)/per;
	int tmp;
	int pre = NewNode(ch);

	//如果是尾塊 直接從表尾開始找
	if(key*per > len)
	{
		tmp = end;
		for(int i = pos; i < len; ++i) tmp = nd[tmp].pre;
	}
	//否則從所在塊的分割點往前找到插入點
	else
	{
		tmp = iter[key];
		int num = pos%per;
		if(num) num = per-num;
		while(num--) tmp = nd[tmp].pre;
	}

	//插入該字元
	nd[pre].pre = nd[tmp].pre;
	nd[tmp].pre = pre;


	//該字元以後的分割點均向前推進一個字元
	while(key*per <= len)
	{
		iter[key] = nd[iter[key]].pre;
		key++;
	}

	len++;
	//如果尾塊達到上限 在結尾開闢一個新的分割點
	if(len%per == 0) iter[len/per] = end;
}

int main()
{
	//fread();
	//fwrite();

	int n,pos;
	char ch[2],opt[2];
	while(~scanf("%s",str))
	{
		len = strlen(str);
		tp = 0;

		//初始化表頭
		head = NewNode(' ');
		int pre = head;

		//把字串存入塊狀連結串列
		for(int i = 0; i < len; ++i)
		{
			int tmp = NewNode(str[i]);
			nd[tmp].pre = pre;
			pre = tmp;
			//每當出現一個新塊(分割點) 記錄下來
			if(i%per == 0) iter[i/per] = tmp;
		}
		//建立表尾
		end = NewNode(' ');
		nd[end].pre = pre;
		if(len%per == 0) iter[len/per] = end;

		scanf("%d",&n);
		while(n--)
		{
			scanf("%s",opt);
			if(opt[0] == 'Q')
			{
				scanf("%d",&pos);
				printf("%c\n",Search(pos-1));
			}
			else
			{
				scanf("%s%d",ch,&pos);
				Add(pos-1,ch[0]);
			}
		}
	}

	return 0;
}