1. 程式人生 > 實用技巧 >#字尾陣列,單調佇列#洛谷 2852 [USACO06DEC]Milk Patterns G

#字尾陣列,單調佇列#洛谷 2852 [USACO06DEC]Milk Patterns G

題目

給定一個長度為\(n\)的字串,求出現至少\(k\)次的最長子串長度


分析

由於字尾排序後的LCP才是最長的,既然要求至少\(k\)次,
實際上也就是維護長度為\(k\)的height陣列最小值,可以用單調佇列實現


程式碼

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=20011; int ans;
int C[N],sa[N],tp[N],rk[N],height[N],n;
int M,m,k,a[N],b[N],head,tail,q[N];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void SSort(){
	for (rr int i=0;i<=M;++i) C[i]=0;
	for (rr int i=1;i<=n;++i) ++C[rk[tp[i]]];
	for (rr int i=1;i<=M;++i) C[i]+=C[i-1];
	for (rr int i=n;i;--i) sa[C[rk[tp[i]]]--]=tp[i];
}
inline void Suffix_Array(){
	for (rr int i=1;i<=n;++i) rk[i]=a[i],tp[i]=i;
	M=m; SSort();
	for (rr int len=1,p=1;p<n;M=p,len<<=1){
		p=0;
		for (rr int i=n-len+1;i<=n;++i) tp[++p]=i;
		for (rr int i=1;i<=n;++i) if (sa[i]>len) tp[++p]=sa[i]-len;
		SSort(),swap(tp,rk),rk[sa[1]]=p=1;
		for (rr int i=2;i<=n;++i) rk[sa[i]]=(p+=tp[sa[i]]!=tp[sa[i-1]]||tp[sa[i]+len]!=tp[sa[i-1]+len]);
	}
	rr int j=0,k;
	for (rr int i=1;i<=n;height[rk[i]]=j,++i)
	    for (j=j+!j-1,k=sa[rk[i]-1];a[i+j]==a[j+k];++j);
}
signed main(){
	n=iut(),k=iut();
    for (rr int i=1;i<=n;++i) b[i]=a[i]=iut();
    sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
    for (rr int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+m,a[i])-b;
    Suffix_Array(),head=1;
    for (rr int i=1;i<=n;++i){
        while (head<=tail&&q[head]<=i-k+1) ++head;
        while (head<=tail&&height[q[tail]]>=height[i]) --tail; q[++tail]=i;
		if (i>=k-1&&head<=tail) ans=max(ans,height[q[head]]);
    }
	return !printf("%d",ans);
}