题目传送门
题意:
个数的序列
,问你出现次数至少为
的子序列的最大长度。
数据范围: 。
题解:
首先贪心的思考一下,把题意中的“至少”二字去掉。
这个相当于问 的最大值。
那我们掏出后缀数组的板子,然后求出 数组。
然后我们用单调队列维护一下区间最小值的最大值就好了。
感受:
看了之后立刻想到了和题解一样的解法。
但是说实话我第一反应不是实现它,还是hack我自己。
不自信。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 5 ;
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
int s[maxn] ;
int q[maxn] ;
void suffixarray(int n , int m)
{
n ++ ;
for(int i = 0 ; i < n * 2 + 5 ; i ++)
rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间
for(int i = 0 ; i < m ; i ++) cnt[i] = 0 ;
for(int i = 0 ; i < n ; i ++) cnt[rk[i] = s[i]] ++ ;
for(int i = 1 ; i < m ; i ++) cnt[i] += cnt[i - 1] ;
for(int i = 0 ; i < n ; i ++) sa[-- cnt[rk[i]]] = i ;
for(int k = 1 ; k <= n ; k <<= 1)
{
int j = 0 ;
for(int i = 0 ; i < n ; i ++)
{
j = sa[i] - k ;
if(j < 0) j += n ;
tmp[cnt[rk[j]] ++] = j ;
}
sa[tmp[cnt[0] = 0]] = j = 0 ;
for(int i = 1 ; i < n ; i ++)
{
if(rk[tmp[i]] != rk[tmp[i - 1]]
|| rk[tmp[i] + k] != rk[tmp[i - 1] + k])
cnt[++ j] = i ;
sa[tmp[i]] = j ;
}
memcpy(rk , sa , n * sizeof(int)) ;
memcpy(sa , tmp , n * sizeof(int)) ;
if(j >= n - 1) break ;
}
height[0] = 0 ;
for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
while(~k && s[i] != s[sa[j - 1] + k])
height[j] = k -- , j = rk[sa[j] + 1] ;
}
int main()
{
int n , k ;
int num = 1e6 ;
int ans = 0 , l = 1 , r = 0 ;
scanf("%d%d" , &n , &k) ;
for(int i = 0 ; i < n ; i ++) scanf("%d" , &s[i]) ;
if(n < k){printf("0\n") ; return 0 ;}
suffixarray(n , num) ;
for(int i = 2 ; i <= k ; i ++)
{
while(l <= r && height[q[r]] > height[i]) r -- ;
q[++ r] = i ;
}
ans = max(ans , height[q[l]]) ;
for(int i = k + 1 ; i <= n ; i ++)
{
while(l <= r && q[l] <= i - (k - 1)) l ++ ;
while(l <= r && height[q[r]] > height[i]) r -- ;
q[++ r] = i ;
ans = max(ans , height[q[l]]) ;
}
printf("%d\n" , ans) ;
return 0 ;
}