LuoguT68736
[LuoguT68736]
犯SB错误调了一早上
本题为Luogu2019冬省选班期末测试T2
题目大意是求最长的不降长度为\(k\)的不重合子串的数量.
30opts:
我们设\(f_i\)表示以\(i\)结尾的长度为\(k\)的子串序列最长长度.每次暴力转移就好了
\(O(Tn ^ 3)\)
60opts:
发现这个东西也是可以利用树状数组进行优化的.
但前提是我们应该知道所有长度为\(k\)的子串的排名,暴力排序很明显是\(O(n^3)\)的,我们考虑优化。
使用二分+hash 二分\(lcp\)的最后一个字符,然后判断下一个是和否完全相同
\(hash\)值可以\(O(n)\)预处理,\(O(1)\)求出,所以排序的时间复杂度就是\(O(nlog^2n)\)
然后放到树状数组中
\(v_i\)表示\(i\)后缀的前\(k\)个字符的排名.用树状数组来维护排名为\(1 - n\)的最大的$ f_i $
因为有不能重复的限制
到达\(i\)时,再讲\(i - k\)的贡献插入到树状数组中,同时直接查询小于等于它的排名的最大的\(f_i\)
就好了
\(O(Tnlog^2n)\)
100opts:
将二分+hash排序的过程改为后缀数组,之后利用后缀数组给所有长度为\(k\)的子串排序,就好了
\(O(Tnlogn)\)
最后说一下自己犯的错误
多组数据应该清空\(y\)数组(memset0)
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 3;
int x[N],y[N],sa[N],c[N];
int rk[N],height[N];
char s[N];
int g[N],f[N],val[N];
int T,n,k;
inline void SA(){
int m = 128;
memset(rk,0,sizeof(rk));
memset(y,0,sizeof(y));
memset(height,0,sizeof(height));
memset(sa,0,sizeof(sa));
memset(c,0,sizeof(c));
for(int i = 1;i <= n;++i) x[i] = s[i],c[x[i]]++;
for(int i = 1;i <= m;++i) c[i] += c[i - 1];
for(int i = n;i >= 1;--i) sa[c[x[i]]--] = i;
m = 0;
for(int w = 1;m < n;w <<= 1){
// cout << w << endl;
m = 0;
for(int i = n - w + 1;i <= n;++i) y[++m] = i;
for(int i = 1;i <= n;++i) if(sa[i] > w) y[++m] = sa[i] - w;
for(int i = 1;i <= m;++i) c[i] = 0;
for(int i = 1;i <= n;++i) c[x[i]]++;
for(int i = 1;i <= m;++i) c[i] += c[i - 1];
for(int i = n;i >= 1;--i) sa[c[x[y[i]]]--] = y[i];
for(int i = 1;i <= n;++i) y[i] = x[i];
x[sa[1]] = m = 1;
// cout << w << endl;
for(int i = 2;i <= n;++i){
if(y[sa[i]] == y[sa[i - 1]] && y[sa[i] + w] == y[sa[i - 1] + w]) x[sa[i]] = m;
else x[sa[i]] = ++m;
}
}
// cout << 1 << endl;
for(int i = 1;i <= n;++i) rk[sa[i]] = i;
// for(int i = 1;i <= n;++i) printf("%d ",sa[i]);puts("");
int kk = 0;
for(int i = 1;i <= n;++i){
if(kk) kk--;
int j = sa[rk[i] - 1];
while(s[j + kk] == s[i + kk]) kk++;
height[rk[i]] = kk;
}
// for(int i = 1;i <= n;++i) printf("%d ",height[i]);puts("");
}
inline void updata(int x,int v){
for(;x <= n;x += x & -x) g[x] = max(g[x],v);
}
inline int query(int x){
int res = 0;
for(;x;x -= x & -x) res = max(res,g[x]);
return res;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);scanf("%s",s + 1);
SA();
memset(f,0,sizeof(f));memset(g,0,sizeof(g));memset(val,0,sizeof(val));
int p = 0;
for(int i = 1;i <= n;++i){
if(height[i] < k) p++;
val[sa[i]] = p;
}
int ans = 0;
for(int i = 1;i <= n - k + 1;++i){
if(i <= k) f[i] = 1;
else{
updata(val[i - k],f[i - k]);
f[i] = query(val[i]) + 1;
// printf("%d\n",f[i]);
}
ans = max(ans,f[i]);
}
printf("%d\n",ans);
}
return 0;
}