[DP][SA][可持久化线段树]黑红兔

  • 源自 xyz32768 菜鸡的 FJ 省冬令营模拟赛题

  • 原题 CF1063F

Statement

  • 给定一个长度为 \(n\) 的字符串 \(s\),仅包含小写英文字母

  • 要从中从左往右选出若干段不相交的子串

  • 使得选出的这些串中,每个串都是上一个串的严格子串

  • 求最多能选出多少段

  • \(1\le n\le5\times10^5\)

Solution

  • 首先注意到一个性质:设选出的第一个子串长度为 \(len\),那么最优解选出的子串长度一定是 \(len,len-1,\dots,2,1\)

  • 于是从右往左 DP:\(f[i][j]\) 表示以 \(i\) 为首串的开头,是否存在选出 \(j\) 段的方案(也是首串的长度为 \(j\)

  • 这个 DP 是 \(O(n^2)\)

  • 我们还有一个性质:\(f[i][j]\) 是单调的。换句话说,如果存在选出 \(j\) 个串的方案就一定存在选出 \(j-1\) 个串的方案

  • 证明考虑第一个串 \(t\) ,如果选出的前 \(k\) 个串都是 \(t\) 的后缀,而第 \(k+1\) 个串是第 \(k\) 个串的前缀,那么我们可以把前 \(k\) 个串都删掉最后一个字符,第 \(k+1\) 个串直接扔掉,这样就构造出了一个选 \(j-1\) 个串的方案,特殊情况 \(k=j\) 时把所有串都删掉最后一个字符并把最后一个串(长度已经变成 \(0\))扔掉

  • 于是可以重新定义 \(f[i]\) 表示以 \(i\) 为开头最多选出多少个串

  • 根据上面的结论,可以二分 \(f[i]\),转化成判断是否存在 \(j\in[i+f[i]\dots n]\) 使得 \(\max(\text{lcp}(i,j),\text{lcp}(i+1,j))\ge f[i]-1\)\(f[j]\ge f[i]-1\)

  • 易得建出 SA 之后,满足 \(\text{lcp}(i,j)\ge f[i]-1\)\(rank_j\) 是一段区间,可以二分 + RMQ 求出这个区间之后,利用可持久化线段树求得该区间内的 \(f[j]\) 最大值,\(\text{lcp}(i+1,j)\) 同理

  • 这样的复杂度是 \(O(n\log^2n)\)

  • 注意到另一个性质:\(f[i+1]\ge f[i]-1\),证明类似上一个结论

  • 于是把 \(f[i]\) 设成 \(f[i+1]+1\) 之后不断检查当前的 \(f[i]\) 是否合法,如果不合法就一直 \(f[i]--\)

  • 易得 \(f[i]--\) 的次数之和不超过 \(O(n)\),所以总复杂度 \(O(n\log n)\)

  • 现场 \(O(n\log^2n)\) 甚至 \(O(n\sqrt n)\) 小常数过了,声名狼藉 QAQ

Code

  • 咕咕咕
posted @ 2020-01-23 11:41  epic01  阅读(439)  评论(0编辑  收藏  举报