Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 256 Solved: 119
[Submit][Status][Discuss]
Description
墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势。股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况。经过长时间的观测,墨墨发现很多股票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A部分的股价和C部分的股价的走势如出一辙。通过这个观测,墨墨认为他可能找到了一个预测股票未来走势的方法。进一步的研究可是难住了墨墨,他本想试图统计B部分的长度与发生这种情况的概率关系,不过由于数据量过于庞大,依赖人脑的力量难以完成,于是墨墨找到了善于编程的你,请你帮他找一找给定重现的间隔(B部分的长度),有多少个时间段满足首尾部分的走势完全相同呢?当然,首尾部分的长度不能为零。
Input
输入的第一行包含两个整数N、M,分别表示需要统计的总时间以及重现的间隔(B部分的长度)。接下来N行,每行一个整数,代表每一个时间点的股价。
Output
输出一个整数,表示满足条件的时间段的个数
Sample Input
12 4
1 2 3 4 8 9 1 2 3 4 8 9
Sample Output
6
【样例说明】
6个时间段分别是:3-9、2-10、2-8、1-9、3-11、4-12。
HINT
对于100%的数据,4≤N≤50000 1≤M≤10 M≤N 所有出现的整数均不超过32位含符号整数。
把原来的数差分一下,就变成了求给定 B 的长度的 ABA 这种形式的子串的个数。
暴力的方法肯定可以枚举 A 的长度,然后在枚举第一个A的起点,判断是否可行。这样是 O(n2) 的。
这个做法可以优化一下。
每次在枚举第一个 A 的起点的时候,我们不一定要枚举n个位置。假设当前枚举的长度是 x ,可以吧这个串每 x 个分成一份。枚举每个组的开头。假设当前枚举的位置是 i 。那么第二个 A 的起点就是 i+x+m 。这样只需要看一下从这两个位置同时往前或者往后有多少一样的(也就是分别向前向后求 lcp )。最后把答案加上这两个长度的和就行了。
只是求 lcp 会有重复的情况,所以向左向右找的最大长度都不能超过 x 。
时间复杂度: O(nlogn)
#include
#include
#include
#include
using namespace std;
#define LL long long
const int N=100010;
LL ans;
int s[N],n,m,o,sa[N],rank[N],height[N],t1[N],t2[N],c[N],st[N][20],Log[N],a[N];
inline int in(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline bool cmp(int *y,int p,int q,int k){
int o0=(p+k>=n+n+1)?-1:y[p+k];
int o1=(q+k>=n+n+1)?-1:y[q+k];
return o0==o1&&y[p]==y[q];
}
inline void build_sa(int o){
int i,k,*x=t1,*y=t2,p;
for(i=0;i0;
for(i=0;i1;++i) ++c[x[i]=s[i+1]];
for(i=1;i1];
for(i=n+n;~i;--i) sa[--c[x[i]]]=i;
for(k=1;k<=n+n+1;k<<=1){
for(p=0,i=n+n+1-k;i1;++i) y[p++]=i;
for(i=0;i1;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i0;
for(i=0;i1;++i) ++c[x[y[i]]];
for(i=1;i1];
for(i=n+n;~i;--i) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
o=1;x[sa[0]]=0;
for(i=1;i1;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?o-1:o++;
if(o>=n+n+1) break;
}
}
inline void build_height(){
int i,k=0,j;
for(i=0;i1;++i) rank[sa[i]]=i;
for(i=0;i1;++i){
if(!rank[i]) continue;
k=k?--k:k;
j=sa[rank[i]-1];
while(s[j+k+1]==s[i+k+1]) ++k;
height[rank[i]]=k;
}
memset(st,127/3,sizeof(st));
for(i=0;i1;++i) st[i][0]=height[i];
for(j=1;j<=20;++j)
for(i=0;i+(1<<(j-1))1;++i)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
for(j=0,i=1;i<=n+n+1;++i){
if((1<<(j+1))<=i) ++j;
Log[i]=j;
}
}
inline int LCP(int x,int y){
if(x>y) swap(x,y);
int k=Log[y-x];++x;
return min(st[x][k],st[y-(1<1][k]);
}
int main(){
int i,j;
n=in();m=in();--n;
for(i=1;i<=n+1;++i) s[i]=in();
for(i=1;i<=n;++i) s[i]=s[i+1]-s[i];
for(i=n;i;--i) s[n+n+1-i]=s[i];
for(i=1;i<=n+n;++i) a[i]=s[i];
sort(a+1,a+n+n+1);
int size=unique(a+1,a+n+n+1)-a-1;
for(i=1;i<=n;++i)
s[i]=lower_bound(a+1,a+size+1,s[i])-a;
for(i=n+n;i>=n+1;--i)
s[i+1]=lower_bound(a+1,a+size+1,s[i])-a;
s[n+1]=size+1;
build_sa(size+2);
build_height();
int pre,sub;
for(i=1;i+i+m<=n;++i)
for(pre=0,j=1;i+j+m<=n;j+=i){
sub=min(i,LCP(rank[j-1],rank[j+i+m-1]));
if(sub+pre>=i) ans+=(LL)(sub+pre-i+1);
pre=min(i-1,LCP(rank[n+n+2-(j+i-1)-1],rank[n+n+2-(j+i+i+m-1)-1]));
}
printf("%lld\n",ans);
}