题意:
给你一个长度为
n
n
n的字符串,有
m
m
m次询问,每次询问给你四个参数
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,问你字串
s
[
a
.
.
.
b
]
s[a...b]
s[a...b]中的所有字串与字串
s
[
c
.
.
,
d
]
s[c..,d]
s[c..,d]的最长公共前缀的长度。
n
,
m
<
=
1
e
5
n,m<=1e5
n,m<=1e5,字符集是小写字母
题解:
兴冲冲地去你谷找主席树的黑题,结果发现四道写过两个,其中IOI那个写的是线段树合并,然后这个好像又不是个难题。
由于退役太久,现在不会SA了,然后比较气人和自闭的是曾经拿手的线段树合并不会了,特别是曾经做过不少的SAM上的线段树合并。于是这题我想用主席树。由于还是口胡的,我仍然不保证正确性。
具体做法是,字串最长公共前缀直接做不好做,很自然的一个想法是把串翻转,然后转化为两个子串的最长公共后缀。主要原因是我们处理字串的强大数据结构都是后缀数据结构(SA/SAM)。
我们对翻转的串建SAM,记录每个位置结尾的后缀对应的SAM中的点,询问的时候 s [ c . . . d ] s[c...d] s[c...d]只需要找到反串上 d d d对应的后缀在SAM上的位置,然后倍增就能找到,字串定位也算是SAM常规操作吧。然后就是考虑 s [ a . . . b ] s[a...b] s[a...b]反串后的所有字串和它的最长公共后缀怎么算。
我想的是相当于在 d d d的那个后缀跳到 c c c后到根的那条链上,找一个离 c c c最近的点,使得其他那些字串存在一个点能跳到这个点,并且一段的长度还在 [ a , b ] [a,b] [a,b]里。
具体做法是,用主席树维护dfs序来实现维护子树,然后通过前缀相减维护出一个子树的信息,然后主席树的下标 i i i表示反串中第 i i i个字符为结尾的前缀是否有一个字串对应了树上dfs序的这个点,有是 1 1 1无是 0 0 0,然后维护区间和。
这样对于每个前缀,在SAM上定位之后都可以在parent树上用差分标记来维护出它有影响的这条链。然后对于每个询问,我们回答的办法是先二分答案,因为这个公共长度直接求不好求,但是又显然满足二分性。二分了之后在 s [ c . . . d ] s[c...d] s[c...d]定位到的点树上倍增跳到这个点,然后用在这个二分值对应算出哪些 s [ a . . . b ] s[a...b] s[a...b]区间位置的前缀能在区间里有这么长的公共部分,然后在这个点的子树的对应前缀相减后的主席树中找合法端点区间看区间和是不是大于0,就可以判断当前二分的答案是否可行。
听说线段树合并也是可以做的,但是现在忘了(为什么我人菜还记性这么差)。
这个做法可能有点复杂,有点蠢,甚至不一定对,而且好像不是太好写的样子,估计200行起步?
复杂度是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
还是暂时没有代码。