「HAOI2018」字串覆盖
题目描述
小C对字符串颇有研究,他觉得传统的字符串匹配太无聊了,于是他想到了这样一个问题.
对于两个长度为 $n$ 的串 $A, B$ , 小C每次会给出给出 $4$ 个参数 $s, t, l, r$ . 令 $A$ 从 $s$ 到 $t$ 的子串(从 $1$ 开始标号)为 $T$,令 $B$ 从 $l$ 到 $r$ 的子串为 $P$.然后他会进行下面的操作:
如果 $T$ 的某个子串与 $P$ 相同,我们就可以覆盖 $T$ 的这个子串,并获得 $K - i$ 的收益,其中 $i$ 是初始时 $A$ 中(注意不是 $T$ 中)这个子串的起始位置,$K$是给定的参数.一个位置不能被覆盖多次.覆盖操作可以进行任意多次,你需要输出获得收益的最大值.
注意每次询问都是独立的,即进行一次询问后,覆盖的位置会复原.
输入格式
第一行两个整数 $n, K$ ,表示字符串长度和参数.
接下来一行一个字符串 $A$.
接下来一行一个字符串 $B$ .
接下来一行一个整数 $q$ ,表示询问个数.
接下来 $q$ 行,每行四个整数 $s, t, l, r$ ,表示一次询问.
输出格式
输出 $q$ 行,每行一个整数,表示一个询问的答案.
数据范围与提示
对于所有数据,有 $1 \le n, q \le 10^5$ ,$A, B$ 仅由小写英文字母组成,$1 \le s \le t \le n, 1 \le l \le r \le n, n < K \le 10^9$.
对于 $n = 10^5$ 的测试点,满足 $51 \le r - l \le 2000$ 的询问不超过 $11000$ 个,且 $r - l$ 在该区间内均匀随机.
貌似有后缀数组的优秀做法,但是蒟蒻只会后缀自动机……
首先容易想到,要获得最大收益,一定是采取这样的贪心策略:找到当前区间中左端点最小的用来覆盖的子串,再找到与它不交的下一个左端点最小的用来覆盖的子串……直到不能找到为止。
正确性说明:
由于$K>n$,所以每次收益一定是正的,那么单次价值显然是越靠前出现的越大。考虑这样一种情况:区间中第二靠左的子串与最左边的子串有交。那么如果要选第二的子串,那么剩下子串的选取方案对于最靠左的子串也是合法的。因此,每次选最靠左的一定是最优的。
那么现在只需要找到这些不交的出现位置即可,发现由于“不交”的限制很难直接用数据结构维护。那么考虑设置阈值(其实数据范围也有提示)。
用来覆盖的子串长度大于阈值时,用SAM暴力跳。具体做法是在两个串中间加一个分隔符建立SAM,用线段树合并弄出right集合。每次询问,用倍增找到自动机上$P[l,r]$对应的状态,再不断在它对应的线段树上找一找某个区间里出现的第一个位置就好。
当这个长度小于阈值的时候,考虑建图:用$id[i][j]$表示$T$中以$i$开头,长度为$j$的字符串代表的状态(这样的字符串需要是某个用来覆盖的串,否则没有意义),每个点向它右边第一个与它完全相同的字符串代表的状态连边,如果是第一次出现就连向0号点。容易看出这样形成了一棵树。对于每次询问,只需要倍增找到对应区间即可。
连边的处理需要开一个链表。在这里为了知道某个字符串右边第一次出现的位置,可以使用字符串hash+hash表(代码里采用的unordered_map)。
细节很多,码量很大,写起来很爆炸。考试时遇到这种题,应该先敲暴力,在前两道题都比较稳的情况下再写正解吧……
但是考场上真的能写对吗……
1 |
|