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

Sample Output

6

解题思路:

还是很佩服后缀数组的思维。

这是求一个具有ABA结构的字符串个数。

发现一个性质,如果左右A足够长,

就可以将中间部分左右移动得到新答案。

枚举A的长度L,按L分块,边缘设为关键点。

易知关键点只能被一个串覆盖一次。

只需正反构建后缀数组求Lcp即可。

时间复杂度n(lnn+logn)

代码:

 #include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=;
struct Sa{
int sa[N];
int tmr[N];
int rnk[N];
int has[N];
int str[N];
int hgt[N];
int Rmq[][N];
int lg[N];
int cnt;
int n;
bool Same(int a,int b,int l)
{
if(a+l>n||b+l>n)
return false;
return (rnk[a]==rnk[b])&&(rnk[a+l]==rnk[b+l]);
}
void Insert(int *a,int len)
{
for(int i=;i<=len;i++)
str[i]=a[i];
n=len;
return ;
}
void Reverse(int *a,int len)
{
int j=;
for(int i=len;i;i--)
str[++j]=a[i];
n=len;
return ;
}
void Build(void)
{
for(int i=;i<=n;i++)
lg[i]=lg[i/]+;
for(int i=;i<=n;i++)
has[str[i]]++;
for(int i=;i<=n;i++)
if(has[i])
tmr[i]=++cnt;
for(int i=;i<=n;i++)
has[i]+=has[i-];
for(int i=;i<=n;i++)
{
sa[has[str[i]]--]=i;
rnk[i]=tmr[str[i]];
}
for(int k=;cnt!=n;k<<=)
{
cnt=;
for(int i=;i<=n;i++)
has[i]=;
for(int i=;i<=n;i++)
has[rnk[i]]++;
for(int i=;i<=n;i++)
has[i]+=has[i-];
for(int i=n;i;i--)
if(sa[i]>k)
tmr[sa[i]-k]=has[rnk[sa[i]-k]]--;
for(int i=;i<=k;i++)
tmr[n-i+]=has[rnk[n-i+]]--;
for(int i=;i<=n;i++)
sa[tmr[i]]=i;
for(int i=;i<=n;i++)
if(Same(sa[i],sa[i-],k))
tmr[sa[i]]=cnt;
else
tmr[sa[i]]=++cnt;
for(int i=;i<=n;i++)
rnk[i]=tmr[i];
}
for(int i=;i<=n;i++)
{
if(rnk[i]==)
continue;
int j=std::max(,hgt[rnk[i-]]-);
while(str[i+j-]==str[sa[rnk[i]-]+j-])
hgt[rnk[i]]=j++;
}
for(int i=;i<=n;i++)
Rmq[][i]=hgt[i];
for(int i=;i<=;i++)
for(int j=;j+(<<i)-<=n;j++)
Rmq[i][j]=std::min(Rmq[i-][j],Rmq[i-][j+(<<(i-))]);
return ;
}
int Lcp(int i,int j)
{
i=rnk[i],j=rnk[j];
if(j<i)
i^=j^=i^=j;
i++;
int l=lg[j-i+];
return std::min(Rmq[l][i],Rmq[l][j-(<<l)+]);
}
}S1,S2;
int tmp[N];
int sln[N];
int n,B;
std::map<int,int>M;
int main()
{
//freopen("a.in","r",stdin);
scanf("%d%d",&n,&B);
int cnt=;
for(int i=;i<=n;i++)
scanf("%d",&sln[i]);
for(int i=n;i>=;i--)
sln[i]-=sln[i-];
for(int i=;i<=n;i++)
{
if(M.find(sln[i])==M.end())
M[sln[i]]=++cnt;
tmp[i-]=M[sln[i]];
}
S1.Insert(tmp,n-);
S2.Reverse(tmp,n-);
S1.Build();
S2.Build();
int ans=;
for(int L=;L<=((n-)-B)/;L++)
{
for(int i=;i<=n-;i+=L)
{
int j=i+B+L;
if(j>n-)
continue;
int r=std::min(L,S1.Lcp(i,j));
int l=std::min(L,S2.Lcp(n-i,n-j));
int t=l+r-;
if(t>=L)
ans+=t-L+;
}
}
printf("%d\n",ans);
return ;
}

BZOJ2119: 股市的预测(后缀数组)的更多相关文章

  1. 【BZOJ2119】股市的预测 后缀数组+分块

    [BZOJ2119]股市的预测 Description 墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势.股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰 ...

  2. 【BZOJ-2119】股市的预测 后缀数组

    2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 334  Solved: 154[Submit][Status][Discuss ...

  3. BZOJ 2119: 股市的预测 [后缀数组 ST表]

    2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 331  Solved: 153[Submit][Status][Discuss ...

  4. BZOJ 2119 股市的预测 (后缀数组+RMQ)

    题目大意:求一个字符串中形如$ABA$的串的数量,其中$B$的长度是给定的 有点像[NOI2016]优秀的拆分这道题 先对序列打差分,然后离散,再正反跑$SA$,跑出$st$表 进入正题 $ABA$串 ...

  5. bzoj千题计划312:bzoj2119: 股市的预测(后缀数组+st表)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2119 题意:将给定数组差分后,求ABA形式的字串个数,要求|B|=m,|A|>0 1.后缀数 ...

  6. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  7. BZOJ2119 股市的预测 字符串 SA ST表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9069171.html 题目传送门 - BZOJ2119 题意 给定一个股票连续$n$个时间点的价位,问有多少段 ...

  8. bzoj2119 股市的预测

    传送门 感觉智商莫名其妙的就变低了……写这题的时候死活想不出来…… 做法其实不难…… 题目要求形如ABA的串的个数,我们可以枚举A的长度,利用标记关键点的方法统计答案.设枚举到的答案为k,每k个点标记 ...

  9. bzoj 4650(洛谷 1117) [Noi2016]优秀的拆分——枚举长度的关键点+后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...

随机推荐

  1. HIT 2255 Not Fibonacci(矩阵高速幂)

    #include <iostream> #include <cstdio> #include <cstring> using namespace std; cons ...

  2. vue24-webpack+vue-loader

    手动配置自己: webpack+vue-loader webpack加载模块 ------------------------------------- 如何运行此项目? 1. npm install ...

  3. url与图片

    http://restapi.amap.com/v3/staticmap?location=116.481485,39.990464&zoom=10&size=750*300& ...

  4. windows “文件大小”与“占用空间”、文件系统与文件拷贝

    0. 文件大小与占用空间 "文件大小"和"占用空间"的差别 首先需要明确的是,"文件大小"代表着文件的真实大小(文件内容实际包含的全部字节数 ...

  5. 128.C++文件操作小结

    打开后缀参数 #include <fstream> #include <iostream> using namespace std; //文本读写 //文件写入 void ma ...

  6. 分享一个js对象开发组件写法

    var TextCount = (function(){ //私有方法,外面将访问不到 var _bind = function(that){ that.input.on('keyup',functi ...

  7. LeetCode 258 Add Digits(数字相加,数字根)

    翻译 给定一个非负整型数字,反复相加其全部的数字直到最后的结果仅仅有一位数. 比如: 给定sum = 38,这个过程就像是:3 + 8 = 11.1 + 1 = 2.由于2仅仅有一位数.所以返回它. ...

  8. js中的this详解

    在web前端开发中,javascript中的this和其他的JAVA,C#等大型语言一样,是一个重要概念.但是要注意的是,在javascript中,由于 javascript的动态性,this的指向在 ...

  9. 微信小程序,前端大梦想(二)

    微信小程序之数据缓存实例-备忘录     数据缓存在移动端的使用是非常重要的,既可以减少用户的流量支出又可以提高程序的访问速度和用户体验.每个微信小程序都可以有自己的本地缓存,可以通过 wx.setS ...

  10. mv---移动文件或目录

    mv命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中.source表示源文件或目录,target表示目标文件或目录.如果将一个文件移到一个已经存在的目标文件中,则目标文件的内容将被覆 ...