首页 > 技术文章 > LuoguT68736

wyxdrqc 2019-04-03 14:39 原文

[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;	
}

推荐阅读