Time Limit: 10 Sec
Memory Limit: 162 MB
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
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11
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
数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
1
0
2
1
数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
题意:有LCQ(x,y)操作询问以当前字符串中第x个开始和第y个开始的最长公共前缀的长度。同时还可以插入一个字符或者修改一个字符。
询问最大长度,想到for。。。但同时想到会超时。。。同时字符串比较灰常慢。。。所以可以用hash+二分查找最长公共前缀。我们可以设计一个较好的hash函数,让每个不同的字符串的hash值尽量不重复。因为有插入和修改序列的操作。所以就要动态维护。用到了splay。
然后每个树中的节点记录自己的父亲fa[i],子节点c[i,0]c[i,1],子树(包括本身)构成的字符串的hash值h[i],本身字符a[i],子树(包括自己)中的节点个数sum[i]。
hash的求法:[ord(a[1])*k^0+ord(a[2])*k^1+ord(a[3])*k^2+...+ord(a[n])*k^(n-1)] mod maxn
那在旋转的时候怎么修改h[i]呢?h[x]:=h[c[x,0]]+a[x]*k^(sum[c[x,0]])+h[c[x,1]]*k^(sum[c[x,0]]+1)
每次二分长度只要把要匹配的子串转成根的右节点的左子树所对应的串。比较两个两棵子树的hash值是否相同判断子串是否相同。
可以加几个特判:
1.判断首字母位置是否相同。相同输出最大长度即可。
2.判断首字母是否是不同字母。不同则输出0。
3.最大长度特判。有的数据整列字符都是一样的。。。。
AC CODE
program
hy_1014;
const
maxn=
9875321
;
var
h:
array
[
0..100010
]
of
int64
;
d:
array
[
1..100010
]
of
int64
;
a,sum:
array
[
0..100010
]
of
int64
;
fa:
array
[
0..100010
]
of
longint
;
ll,rr:
array
[
1..150010
]
of
longint
;
c:
array
[
0..100010
,
0..1
]
of
longint
;
size,k,kk,root,n,m,all:
longint
;
hh,ch:
array
[
1..150010
]
of
char
;
//============================================================================
procedure
update(x:
longint
);
var
p,q:
longint
;
begin
sum[x]:=sum[c[x,
0
]]+sum[c[x,
1
]]+
1
;
p:=c[x,
0
]; q:=c[x,
1
];
h[x]:=(h[p]+a[x]*d[sum[p]+
1
]+h[q]*d[sum[p]+
2
])
mod
maxn; //更新hash值。
end
;
//============================================================================
procedure
build(l,r:
longint
); //一开始用递归建树,防止树的深度太大。
var
mid,tmp:
longint
;
begin
mid:=(l+r)
shr
1
;
if
mid>l
then
begin
build(l,mid-
1
);
tmp:=(l+mid-
1
)
shr
1
;
c[mid,
0
]:=tmp; fa[tmp]:=mid;
end
;
if
mid<r
then
begin
build(mid+
1
,r);
tmp:=(mid+
1
+r)
shr
1
;
c[mid,
1
]:=tmp; fa[tmp]:=mid;
end
; update(mid);
end
;
//============================================================================
procedure
init;
var
i,len:
longint
;
st:
ansistring
;
chh:
char
;
begin
readln(st); len:=length(st);
for
i:=
2
to
len+
1
do //在头尾预留两个单元,splay时就总能找到l-1和r+1。
begin
a[i]:=ord(st[i-
1
])-ord(
'a'
);
h[i]:=a[i];
end
; d[
1
]:=
1
;
for
i:=
2
to
100010
do
d[i]:=(d[i-
1
]*
27
)
mod
maxn; //预处理k的n次方(见hash函数)。
build(
1
,len+
2
); root:=(len+
3
)
shr
1
;
readln(m); size:=len+
2
;
for
i:=
1
to
m
do
begin
read(ch[i]);
if
ch[i]=
'Q'
then
begin
inc(all); readln(ll[i],rr[i]);
end
else
readln(ll[i],chh,hh[i]);
end
;
end
;
//============================================================================
function
find(x:
longint
):
longint
;
begin
if
sum[c[x,
0
]]+
1
=k
then
exit(x);
if
sum[c[x,
0
]]<k-
1
then
begin
dec(k,sum[c[x,
0
]]+
1
);
find:=find(c[x,
1
]);
end
else
find:=find(c[x,
0
]);
end
;
//============================================================================
procedure
rotate(
var
root:
longint
; x:
longint
);
var
y,z,p,q:
longint
;
begin
y:=fa[x]; z:=fa[y];
if
c[y,
0
]=x
then
p:=
0
else
p:=
1
;
q:=p
xor
1
;
if
y=root
then
root:=x
else
if
c[z,
0
]=y
then
c[z,
0
]:=x
else
c[z,
1
]:=x;
fa[x]:=z; fa[y]:=x; fa[c[x,q]]:=y;
c[y,p]:=c[x,q]; c[x,q]:=y;
update(y); update(x);
end
;
//============================================================================
procedure
splay(
var
root:
longint
; x:
longint
);
var
y,z:
longint
;
begin
while
x<>root
do
begin
y:=fa[x]; z:=fa[y];
if
y<>root
then
if
(c[y,
0
]=x)
xor
(c[z,
0
]=y)
then
rotate(root,x)
else
rotate(root,y);
rotate(root,x);
end
;
end
;
//============================================================================
procedure
insert(x:
longint
);
begin
if
x=
0
then
begin
inc(size); h[size]:=kk; sum[size]:=
1
;
a[size]:=h[size]; exit;
end
;
if
sum[c[x,
0
]]>=k
then
begin
insert(c[x,
0
]);
if
c[x,
0
]=
0
then
begin
c[x,
0
]:=size; fa[size]:=x;
end
;
end
else
begin
k:=k-sum[c[x,
0
]]-
1
; insert(c[x,
1
]);
if
c[x,
1
]=
0
then
begin
c[x,
1
]:=size; fa[size]:=x;
end
;
end
; update(x);
end
;
//============================================================================
procedure
ask(x:
longint
);
var
xx,yy,l,r,ans,l1,l2,left1,left2:
longint
;
hash1,hash2,s,t,mid,tt:
longint
;
begin
if
rr[x]<ll[x]
then
begin
tt:=rr[x]; rr[x]:=ll[x]; ll[x]:=tt;
end
;
if
ll[x]=rr[x]
then
begin
writeln
(size-
1
-ll[x]); exit;
end
;
k:=ll[x]; l:=find(root); splay(root,l);
k:=rr[x]; r:=find(root);
k:=ll[x]+
1
; xx:=find(root); k:=rr[x]+
1
; yy:=find(root);
if
a[xx]<>a[yy]
then
begin
writeln
(
'0'
); exit;
end
;
s:=
1
; t:=size-rr[x]-
1
; ans:=
0
;
k:=ll[x]+t+
1
; l1:=find(root);
k:=rr[x]+t+
1
; l2:=find(root);
splay(c[root,
1
],l1); hash1:=h[c[l1,
0
]];
splay(c[root,
1
],r); splay(c[r,
1
],l2); hash2:=h[c[l2,
0
]];
if
hash1=hash2
then
begin
writeln
(t); exit;
end
; //这里特判了最长长度的串。。有的数据太猥琐。。。
dec(t);
repeat
mid:=(s+t)
shr
1
;
k:=ll[x]+mid+
1
; l1:=find(root);
k:=rr[x]+mid+
1
; l2:=find(root);
splay(c[root,
1
],l1); hash1:=h[c[l1,
0
]];
splay(c[root,
1
],r); splay(c[r,
1
],l2); hash2:=h[c[l2,
0
]];
if
hash1=hash2
then
begin
s:=mid+
1
; ans:=mid;
end
else
t:=mid-
1
;
until
s>t;
writeln
(ans);
end
;
//============================================================================
procedure
rep(x:
longint
);
var
z,p,q:
longint
;
begin
k:=ll[x]+
1
; z:=find(root); splay(root,z);
a[z]:=ord(hh[x])-ord(
'a'
); p:=c[root,
0
]; q:=c[root,
1
];
h[z]:=(h[p]+a[z]*d[sum[p]+
1
]+h[q]*d[sum[p]+
2
]);
end
;
//============================================================================
procedure
ins(x:
longint
);
begin
k:=ll[x]+
1
; kk:=ord(hh[x])-ord(
'a'
); insert(root);
splay(root,size);
end
;
//============================================================================
procedure
work;
var
i:
longint
;
begin
for
i:=
1
to
m
do
begin
if
ch[i]=
'Q'
then
begin
ask(i); dec(all);
if
all=
0
then
halt;
end
else
if
ch[i]=
'R'
then
rep(i)
else
ins(i);
end
;
end
;
//============================================================================
begin
init;
work;
end
.