1014: [JSOI2008]火星人prefix

10 篇文章 0 订阅

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 5055   Solved: 1602
[ Submit][ Status][ Discuss]

Description

火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。

Input

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示: 1、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。限制:x不超过当前字符串长度。

Output

对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

Sample Input

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

Sample Output

5
1
0
2
1

HINT

数据规模:

对于100%的数据,满足:

1、 所有字符串自始至终都只有小写字母构成。

2、 M <= 150,000

3、 字符串长度L自始至终都满足L <= 100,000

4、 询问操作的个数不超过10,000个。

对于第1,2个数据,字符串长度自始至终都不超过1,000

对于第3,4,5个数据,没有插入操作。


Source

[ Submit][ Status][ Discuss]




嗯。。。打了好长的代码

插入修改操作用splay的merge和split操作轻松解决。。

然后对于每个splay的结点维护其子树hash值

每次询问二分答案然后将子树拆出来比较hash值即可


一开始hash函数没维护好。。。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn = 1E5 + 10;
typedef unsigned long long UL;
const UL hash = 233;

UL fac[maxn];
char A[maxn];
int len;

class data{
	private:
		struct Node{
			Node *ch[2];
			UL h;
			char c;
			int siz;
		}*tot,*root,pool[maxn];
		
		void maintain(Node *&x) {
			x->siz = 1;
			x->h = x->c;
			if (x->ch[1] != NULL) {
				x->siz += x->ch[1]->siz;
				x->h = (x->h + x->ch[1]->h * hash);
			}
			if (x->ch[0] != NULL) {
				x->siz += x->ch[0]->siz;
				x->h = x->h * fac[x->ch[0]->siz] + x->ch[0]->h;
			}
		}
		
		void rotate(Node *&x,int d) {
			Node *y = x->ch[d];
			x->ch[d] = y->ch[d^1];
			y->ch[d^1] = x;
			maintain(x);
			x = y;
			maintain(x);
		}
		
		void Insert(Node *&x,int rank,char c) {
			if (x == NULL) {
				x = ++tot;
				x->siz = 1;
				x->h = x->c = c;
				return;
			}
			
			int s0 = (x->ch[0] == NULL)?0:x->ch[0]->siz;
			int d;
			if (s0 + 1 >= rank) Insert(x->ch[0],rank,c);
			else Insert(x->ch[1],rank-1-s0,c);
		}
		
		void splay(Node *&x,int rank) {
			int s0 = (x->ch[0] == NULL)?0:x->ch[0]->siz;
			if (s0+1 == rank) return;
			int d1;
			if (s0+1 >= rank) d1 = 0;
			else d1 = 1,rank = rank-s0-1;
			int s1 = (x->ch[d1]->ch[0] == NULL)?0:x->ch[d1]->ch[0]->siz;
			if (s1+1 != rank) {
				int d2;
				if (s1+1 >= rank) d2 = 0;
				else d2 = 1,rank = rank-s1-1;
				splay(x->ch[d1]->ch[d2],rank);
				if (d1 == d2) rotate(x,d1);
				else rotate(x->ch[d1],d2);
			}
			rotate(x,d1);
		}
		
		Node *merge(Node *left,Node *right) {
			splay(left,left->siz);
			left->ch[1] = right;
			maintain(left);
			return left;
		}
		
		void split(Node *o,int k,Node *&left,Node *&right) {
			splay(o,k);
			left = o; 
			right = left->ch[1];
			left->ch[1] = NULL;
			maintain(left);
		}
		
		bool Judge(int L,int a,int b) {
			if (a + L - 1 > len) return 0;
			if (b + L - 1 > len) return 0;
			
			Node *o,*o1,*left,*right;
			split(root,a,left,o);
			split(o,L,o1,right);
			UL h1 = o1->h;
			root = merge(left,merge(o1,right));
			
			split(root,b,left,o);
			split(o,L,o1,right);
			UL h2 = o1->h;
			root = merge(left,merge(o1,right));
			
			return h1 == h2;
		}
		
		int solve(int a,int b) {
			int L,R;
			L = 0; R = 1E5 + 10;
			while (R - L > 1) {
				int mid = (L+R) >> 1;
				if (Judge(mid,a,b)) L = mid;
				else R = mid;
			}
			if (Judge(R,a,b)) return R;
			else return L;
		} 
		
	public:
		data(){
			tot = pool; root = NULL;
		}
		
		void Ins(int pos) {
			char c;
			if (pos >= 0) c = A[pos];
			Insert(root,pos+2,c);
			splay(root,pos+2);
		}
		
		int sol(int a,int b) {
			return solve(a,b);
		}
		
		void Modify(int pos,char c) {
			splay(root,pos+1);
			root->c = c;
			maintain(root);
		}
		
		void Work(int pos,char c) {
			Node *left,*right;
			split(root,pos+1,left,right);
			left->ch[1] = ++tot;
			left->ch[1]->siz = 1;
			left->ch[1]->h = left->ch[1]->c = c;
			maintain(left);
			root = merge(left,right);
		}
};

int m;

char getord() 
{
	char ret = getchar();
	while (ret != 'Q' && ret != 'R' && ret != 'I') ret = getchar();
	return ret;	
}

int main()
{
	#ifdef YZY
		freopen("yzy.txt","r",stdin);
	#endif
	
	fac[0] = 1;
	for (int i = 1; i <= 100000; i++) fac[i] = fac[i-1]*hash;
	scanf("%s",A);
	len = strlen(A);
	static data tree;
	tree.Ins(-1);
	for (int i = 0; i < len; i++)
		tree.Ins(i);
	cin >> m;
	while (m--) {
		char ord = getord();
		if (ord == 'R') {
			int pos; scanf("%d",&pos);
			char Mod = getchar();
			while (Mod < 'a' || Mod > 'z') Mod = getchar();
			tree.Modify(pos,Mod);
		}
		else if(ord == 'I') {
			int pos; scanf("%d",&pos);
			char Mod = getchar();
			while (Mod < 'a' || Mod > 'z') Mod = getchar();
			tree.Work(pos,Mod);
			++len;
		}
		else {
			int l,r; scanf("%d%d",&l,&r);
			printf("%d\n",tree.sol(l,r));
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值