1. 程式人生 > >[bzoj2119]股市的預測

[bzoj2119]股市的預測

2119: 股市的預測

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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
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;i<o;++i) c[i]=0;
    for(i=0;i<n+n+1;++i) ++c[x[i]=s[i+1]];
    for(i=1;i<o;++i) c[i]+=c[i-1];
    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;i<n+n+1;++i) y[p++]=i;
        for(i=0;i<n+n+1;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
        for(i=0;i<o;++i) c[i]=0;
        for(i=0;i<n+n+1;++i) ++c[x[y[i]]];
        for(i=1;i<o;++i) c[i]+=c[i-1];
        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;i<n+n+1;++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;i<n+n+1;++i) rank[sa[i]]=i;
    for(i=0;i<n+n+1;++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;i<n+n+1;++i) st[i][0]=height[i];
    for(j=1;j<=20;++j)
      for(i=0;i+(1<<(j-1))<n+n+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<<k)+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);
}