Solution
我们约定 \(S_i=ws:\),\(S(t,i):=Q_t(S_i)\),\(T(i):=Q_{|w|}(S_i)\)(即 \(S_i\) 所有子序列构成的集合)。
1. 转化问题
手玩小样例。我们就考虑题面中的 \(w=\texttt{aaba},t=3\)。
\\\begin{aligned} S(3,2) \&= \\{\\lambda, \\texttt{a}, \\texttt{b}, \\texttt{ba}, \\texttt{ab}, \\texttt{aa}, \\texttt{aba}\\}\\\\ S(3,1) \&= \\{\\lambda, \\texttt{a}, \\texttt{b}, \\texttt{ba}, \\texttt{ab}, \\texttt{aa}, \\texttt{aba}, \\texttt{aab}, \\texttt{aaa}\\} \\end{aligned} \\
\(S(3,1)\) 比 \(S(3,2)\) 多出了 \({\texttt{aab}},{\texttt{aaa}}\) 两个子序列。这是为什么呢?
后缀 \(\texttt{aaba}\) 可以看成在 \(\texttt{aba}\) 前面加上一个 \(\texttt{a}\)。因此,\(\texttt{aba}\) 中的每个子序列在 \(\texttt{aaba}\) 中仍然存在,而新出现的子序列必须用到最前面的 \(\texttt{a}\)。正是这样的子序列起到了区分 \(S(3,2)\) 和 \(S(3,1)\) 的作用。
从 \(S_{i+1}\) 变成 \(S_i\),只是在前面添加了一个字符 \(w_i\)。添加字符不会让原有子序列消失,因此
\S(t,1)\\supseteq\\dots\\supseteq S(t,n-1)\\supseteq S(t,n) \\
要使 \(S(t,\cdot)\) 两两不等,当且仅当每一对相邻集合都不等。我们设 \(f_i\) 为使 \(S(t,i)\neq S(t,i+1)\) 的最小 \(t\) 值。等价地,\(f_i\) 是在 \(T(i)\) 中出现但在 \(T(i+1)\) 中不出现的最短子序列长度。答案即为 \(\max f_i\)。
2. 计算 \(f_i\)
我们首先考虑二分,但是发现不但至少多一只 \(\log\) 而且 check 极其难写,于是放弃这种方法。
往 \(S_{i+1}\) 前面加一个字符 \(w_i\) 就可以得到下一个后缀 \(S_i\),这种嵌套关系启发我们从后往前扫。设当前扫到 \(w_i\)。
分类讨论:
情况一:如果 \(w_i\) 这个字符第一次出现
那么 \(f_i=1\)。
情况二:\(w_i\) 在位置 \(i\) 之后还会再次出现
设在 \(T(i)\) 中出现但在 \(T(i+1)\) 中不出现的最短子序列为
\s=w_iw_{p_2}w_{p_3}\\dots w_{p_m} \\
由前面的分析,\(p_1=i\) 且 \(m \ge 2\)。我们需要考虑,还需要满足什么条件才能使得 \(s\in T(i)\) 且 \(s\notin T(i+1)\)。
接下来考虑第二个字符的位置 \(p_2\)。为了使 \(s\) 不出现在 \(T(i+1)\) 中,不能让它从位置 \(\operatorname{nxt}(i)\) 或更靠右的同字符位置开始匹配。所以 \(p_2\in i+1,\\operatorname{nxt}(i)\)。
我们枚举 \(p_2\),同时使得子序列 \(w_{p_2}w_{p_3}\dots w_{p_m}\) 必须最后一次出现,在 \(p_2\) 后面不再出现 。因此 \(m-1\ge f_{p_2}\) 且可以取到 \(f_{p_2}\)。
注:枚举子序列起点时要求该子序列首次/最后一次出现是一个常见的套路,这个额外的限制既不影响正确性又往往让题目更好做。
于是得到转移方程
\f_i=1+\\min_{i\
3. 复杂度分析
容易 \(O(n)\) 求出 \(\operatorname{nxt}\)。
对于每种字符,设它出现的位置依次为 \(x_1<x_2<\dots<x_m\)。计算转移时,内层循环的总计算量 \(x_m-x_1<n\)。
对每种字符分别计算,所有转移的总次数为 \(O(n|\Sigma|)\)。因此时间复杂度 \(O(n|\Sigma|)\)。
Code
cpp
#include <bits/stdc++.h>
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define pert(i,a,b) for(int i(a);i>=b;--i)
using namespace std;
constexpr int N=3e6+5,INF=1e9;
string s;
int n,ans,f[N],p[N],l[26];
signed main(){
cin>>s;
n=s.size();
fill(l,l+26,n);
pert(i,n-1,0) p[i]=l[s[i]-'a'],l[s[i]-'a']=i;
pert(i,n-1,0){
if(p[i]==n) f[i]=1;
else{
f[i]=INF;
rept(j,i+1,p[i]) f[i]=min(f[i],f[j]);
++f[i];
}
ans=max(ans,f[i]);
}
cout<<ans;
return 0;
}