【bzoj1717】[Usaco2006 Dec]Milk Patterns 产奶的模式 后缀数组+离散化
题目描述
农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个0到1000000之间的数。并且John记录了N(1<=N<=20000)天的牛奶质量值。他想知道最长的出现了至少K(2<=K<=N)次的模式的长度。比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当K=2时,这个长度为4。
输入
* Line 1: 两个整数 N,K。
* Lines 2..N+1: 每行一个整数表示当天的质量值。
输出
* Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度
样例输入
8 2
1
2
3
2
3
2
3
1
样例输出
4
题解
后缀数组+离散化
题目中m比较大,需要离散化。
对于这类求重复字串长度的问题,正着做比较难做。
我们可以先二分答案,然后将问题转化为是否有k次重复的字串。
假设有k次重复的字串,则一定有连续的k个height大于等于mid。
那么我们就可以在O(n)的时间内进行judge操作,总时间复杂度为O(nlogn),可以通过。
另:这里的judge写法比较特殊。由于height[n]是没有值的,所以一般情况下一定能进行下一步判断。但如果m=0,无论如何也无法进行,所以加上特判。
#include <cstdio> #include <algorithm> using namespace std; #define N 20005 int ws[N] , wv[N] , wa[N] , wb[N] , sa[N] , r[N] , n , m , k; int rank[N] , height[N]; struct data { int num , rn , tnum; }a[20001]; bool cmp1(data a , data b) { return a.num < b.num; } bool cmp2(data a , data b) { return a.rn < b.rn; } void da() { int i , j , p , *x = wa , *y = wb , *t; for(i = 0 ; i < m ; i ++ ) ws[i] = 0;; for(i = 0 ; i < n ; i ++ ) ws[x[i] = r[i]] ++ ; for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1]; for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[x[i]]] = i; for(j = p = 1 ; p < n ; j <<= 1 , m = p) { for(p = 0 , i = n - j ; i < n ; i ++ ) y[p ++ ] = i; for(i = 0 ; i < n ; i ++ ) if(sa[i] - j >= 0) y[p ++ ] = sa[i] - j; for(i = 0 ; i < n ; i ++ ) wv[i] = x[y[i]]; for(i = 0 ; i < m ; i ++ ) ws[i] = 0; for(i = 0 ; i < n ; i ++ ) ws[wv[i]] ++ ; for(i = 1 ; i < m ; i ++ ) ws[i] += ws[i - 1]; for(i = n - 1 ; i >= 0 ; i -- ) sa[--ws[wv[i]]] = y[i]; for(t = x , x = y , y = t , x[sa[0]] = 0 , i = p = 1 ; i < n ; i ++ ) { if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) x[sa[i]] = p - 1; else x[sa[i]] = p ++ ; } } for(i = 1 ; i < n ; i ++ ) rank[sa[i]] = i; for(i = p = 0 ; i < n - 1 ; height[rank[i ++ ]] = p) for(p ? p -- : 0 , j = sa[rank[i] - 1] ; r[i + p] == r[j + p] ; p ++ ); } bool judge(int m) { if(m == 0) return 1; int i , last = 0; for(i = 1 ; i <= n ; i ++ ) { if(height[i] < m) { if(i - last >= k) return 1; last = i; } } return 0; } int main() { int i , le , ri , mi , ans = 0; scanf("%d%d" , &n , &k); for(i = 0 ; i < n ; i ++ ) { scanf("%d" , &a[i].num); a[i].rn = i; } sort(a , a + n , cmp1); for(i = 0 ; i < n ; i ++ ) { if(i == 0 || (i > 0 && a[i].num > a[i - 1].num)) m ++ ; a[i].tnum = m; } sort(a , a + n , cmp2); for(i = 0 ; i < n ; i ++ ) r[i] = a[i].tnum; r[n ++ ] = 0; m ++ ; da(); le = 0; ri = n; while(le <= ri) { mi = (le + ri) >> 1; if(judge(mi)) { ans = mi; le = mi + 1; } else ri = mi - 1; } printf("%d\n" , ans); return 0; }