BZOJ2119: 股市的预测(后缀数组)
Description
墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势。股票折线图是研究股票的必备工
具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况。经过长时间的观测,墨墨发现很多股
票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A部分的股价和C部分的股价的走势
如出一辙。通过这个观测,墨墨认为他可能找到了一个预测股票未来走势的方法。进一步的研究可是难住了墨墨,
他本想试图统计B部分的长度与发生这种情况的概率关系,不过由于数据量过于庞大,依赖人脑的力量难以完成,
于是墨墨找到了善于编程的你,请你帮他找一找给定重现的间隔(B部分的长度),有多少个时间段满足首尾部分
的走势完全相同呢?当然,首尾部分的长度不能为零。
Input
第一行包含两个整数N、M,分别表示需要统计的总时间以及重现的间隔(B部分的长度)。
接下来N行,每行一个整数,代表每一个时间点的股价。
4≤N≤50000 1≤M≤10 M≤N 所有出现的整数均不超过32位含符号整数。
Output
输出一个整数,表示满足条件的时间段的个数
Sample Input
12 4
1 2 3 4 8 9 1 2 3 4 8 9
1 2 3 4 8 9 1 2 3 4 8 9
Sample Output
6
解题思路:
还是很佩服后缀数组的思维。
这是求一个具有ABA结构的字符串个数。
发现一个性质,如果左右A足够长,
就可以将中间部分左右移动得到新答案。
枚举A的长度L,按L分块,边缘设为关键点。
易知关键点只能被一个串覆盖一次。
只需正反构建后缀数组求Lcp即可。
时间复杂度n(lnn+logn)
代码:
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 const int N=100000; 6 struct Sa{ 7 int sa[N]; 8 int tmr[N]; 9 int rnk[N]; 10 int has[N]; 11 int str[N]; 12 int hgt[N]; 13 int Rmq[20][N]; 14 int lg[N]; 15 int cnt; 16 int n; 17 bool Same(int a,int b,int l) 18 { 19 if(a+l>n||b+l>n) 20 return false; 21 return (rnk[a]==rnk[b])&&(rnk[a+l]==rnk[b+l]); 22 } 23 void Insert(int *a,int len) 24 { 25 for(int i=1;i<=len;i++) 26 str[i]=a[i]; 27 n=len; 28 return ; 29 } 30 void Reverse(int *a,int len) 31 { 32 int j=0; 33 for(int i=len;i;i--) 34 str[++j]=a[i]; 35 n=len; 36 return ; 37 } 38 void Build(void) 39 { 40 for(int i=2;i<=n;i++) 41 lg[i]=lg[i/2]+1; 42 for(int i=1;i<=n;i++) 43 has[str[i]]++; 44 for(int i=1;i<=n;i++) 45 if(has[i]) 46 tmr[i]=++cnt; 47 for(int i=1;i<=n;i++) 48 has[i]+=has[i-1]; 49 for(int i=1;i<=n;i++) 50 { 51 sa[has[str[i]]--]=i; 52 rnk[i]=tmr[str[i]]; 53 } 54 for(int k=1;cnt!=n;k<<=1) 55 { 56 cnt=0; 57 for(int i=0;i<=n;i++) 58 has[i]=0; 59 for(int i=1;i<=n;i++) 60 has[rnk[i]]++; 61 for(int i=1;i<=n;i++) 62 has[i]+=has[i-1]; 63 for(int i=n;i;i--) 64 if(sa[i]>k) 65 tmr[sa[i]-k]=has[rnk[sa[i]-k]]--; 66 for(int i=1;i<=k;i++) 67 tmr[n-i+1]=has[rnk[n-i+1]]--; 68 for(int i=1;i<=n;i++) 69 sa[tmr[i]]=i; 70 for(int i=1;i<=n;i++) 71 if(Same(sa[i],sa[i-1],k)) 72 tmr[sa[i]]=cnt; 73 else 74 tmr[sa[i]]=++cnt; 75 for(int i=1;i<=n;i++) 76 rnk[i]=tmr[i]; 77 } 78 for(int i=1;i<=n;i++) 79 { 80 if(rnk[i]==1) 81 continue; 82 int j=std::max(1,hgt[rnk[i-1]]-1); 83 while(str[i+j-1]==str[sa[rnk[i]-1]+j-1]) 84 hgt[rnk[i]]=j++; 85 } 86 for(int i=1;i<=n;i++) 87 Rmq[0][i]=hgt[i]; 88 for(int i=1;i<=17;i++) 89 for(int j=1;j+(1<<i)-1<=n;j++) 90 Rmq[i][j]=std::min(Rmq[i-1][j],Rmq[i-1][j+(1<<(i-1))]); 91 return ; 92 } 93 int Lcp(int i,int j) 94 { 95 i=rnk[i],j=rnk[j]; 96 if(j<i) 97 i^=j^=i^=j; 98 i++; 99 int l=lg[j-i+1]; 100 return std::min(Rmq[l][i],Rmq[l][j-(1<<l)+1]); 101 } 102 }S1,S2; 103 int tmp[N]; 104 int sln[N]; 105 int n,B; 106 std::map<int,int>M; 107 int main() 108 { 109 //freopen("a.in","r",stdin); 110 scanf("%d%d",&n,&B); 111 int cnt=0; 112 for(int i=1;i<=n;i++) 113 scanf("%d",&sln[i]); 114 for(int i=n;i>=2;i--) 115 sln[i]-=sln[i-1]; 116 for(int i=2;i<=n;i++) 117 { 118 if(M.find(sln[i])==M.end()) 119 M[sln[i]]=++cnt; 120 tmp[i-1]=M[sln[i]]; 121 } 122 S1.Insert(tmp,n-1); 123 S2.Reverse(tmp,n-1); 124 S1.Build(); 125 S2.Build(); 126 int ans=0; 127 for(int L=1;L<=((n-1)-B)/2;L++) 128 { 129 for(int i=1;i<=n-1;i+=L) 130 { 131 int j=i+B+L; 132 if(j>n-1) 133 continue; 134 int r=std::min(L,S1.Lcp(i,j)); 135 int l=std::min(L,S2.Lcp(n-i,n-j)); 136 int t=l+r-1; 137 if(t>=L) 138 ans+=t-L+1; 139 } 140 } 141 printf("%d\n",ans); 142 return 0; 143 }