农夫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。
农夫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次的模式的长度
Gold
题解:后缀数组解决可重叠的k次最长重复子串。
首先要利用后缀数组求出sa,rank,h (h[i]表示排名第I的后缀与排名i-1的后缀的最长公共前缀)
然后二分答案,判断是否存在连续至少k-1个位置的h值大于mid
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 20003 #define M 1000003 using namespace std; int n,m,p,q,k; int a[N],v[M],rank[2][N],sa[2][N],h[N],ht[N]; void solve(int sa[N],int rank[N],int sa1[N],int rank1[N]) { for (int i=1;i<=n;i++) v[rank[sa[i]]]=i; for (int i=n;i>=1;i--) if (sa[i]>k) sa1[v[rank[sa[i]-k]]--]=sa[i]-k; for (int i=n-k+1;i<=n;i++) sa1[v[rank[i]]--]=i; for (int i=1;i<=n;i++) rank1[sa1[i]]=rank1[sa1[i-1]]+(rank[sa1[i]]!=rank[sa1[i-1]]||rank[sa1[i]+k]!=rank[sa1[i-1]+k]); } void work() { p=0; q=1; for (int i=1;i<=n;i++) v[a[i]]++; for (int i=1;i<=n;i++) v[i]+=v[i-1]; for (int i=1;i<=n;i++) sa[p][v[a[i]]--]=i; for (int i=1;i<=n;i++) rank[p][sa[p][i]]=rank[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]); k=1; while (k<=n) { solve(sa[p],rank[p],sa[q],rank[q]); p^=1; q^=1; k<<=1; } } void calc() { int k=0; for (int i=1;i<=n;i++) if (rank[p][i]==1) h[rank[p][i]]=0; else { int j=sa[p][rank[p][i]-1]; while (a[i+k]==a[j+k]) k++; h[rank[p][i]]=k; if(k>0) k--; } } int check(int x) { int num=0; for (int i=1;i<=n;i++) if (h[i]>=x) { num++; if (num==m-1) return 1; } else num=0; return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]++; work(); calc(); int head=1; int tail=n; int ans=0; while (head<=tail) { int mid=(head+tail)/2; if (check(mid)) ans=mid,head=mid+1; else tail=mid-1; } printf("%d\n",ans); }