bzoj2119 股市的预测 后缀数组+rmq

该博客讨论了如何利用差分、后缀数组和区间最小值查询(RMQ)解决字符串中ABA模式计数的问题。通过初始化ST表并计算正反向的height和rank,可以实现O(1)查询LCP。博客作者提到,枚举相同走势的长度,并确定关键点,每个A最多覆盖一个关键点。枚举关键点并计算匹配长度,以找到满足条件的子串数量。这是一个涉及字符串处理和算法的难题,博主表示花费了一晚上的时间来解决。

题目其实就是给你一段字符串问你类似ABA这种形式的有多少个,其中B的长度给出。
首先要差分,这个就比较明显了。
然后我们初始化ST表,正反各做一遍SA,求出正反的height和rank。
然后由于suf[a],suf[b]的lcp为min(height[a+1]~height[b])所以用ST表处理可以达到O(1)查询。
然后我们对于原串,枚举走势相同的长度为L,每L个设置一个关键点,可以发现,每一个A一定且至多覆盖一个关键点
枚举关键点i,i和i+B+L往左往右匹配长度l和r(同理r也包括关键点),如果l+r>=L那么这个地方就有l+r-L+1个满足的子串
接下来引用原文。。

为了做到不重复,左右延伸最大长度为L-1

为什么这样就不重复了?我这个煞笔竟然想了好长时间,因为上面的斜体,每个关键点延伸出来的,第一个L一定覆盖了这个关键点,从其他关键点开始的都不覆盖,所以不重复不遗漏

艰难= =搞了一晚上,菜的不行。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--) 
using namespace std;
const int N=5e4+5;
const int INF=1e9;
int B,s[N],mp[N],tot;
int n,m,c[N],t1[N],t2[N];
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
inline int find(int x)
{
    int l=1,r=tot;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (mp[mid]==x)return mid;
        else if (x<mp[mid])r=mid-1;
        else l=mid+1;
    }
    return 0;
}
inline bool cmp(int *r,int a,int b,int j){
    return a+j<=n&&b+j<=n&&r[a]==r[b]&&r[a+j]==r[b+j];
}
int Log[N],Pow[20],mn[N][17];
inline void iniST()
{
    Pow[0]=1;
    fo(i,1,19)Pow[i]=Pow[i-1]*2;
    Log[0]=-1;
    fo(i,1,n)Log[i]=Log[i/2]+1;
}
inline void getST(int mn[N][17],int a[])
{
    fo(i,1,n)mn[i][0]=a[i];
    fo(j,1,Log[n])
    for(int i=1;i+Pow[j]-1<=n;i++)
    {
        mn[i][j]=min(mn[i][j-1],mn[i+Pow[j-1]][j-1]);
    }
}
struct SA{
    int sa[N],rank[N],height[N],mn[N][17];
    inline void getheight()
    {
        int k=0;
        fo(i,1,n)rank[sa[i]]=i;
        fo(i,1,n)
        {
            if (k)k--;
            if (rank[i]==1)continue;
            int j=sa[rank[i]-1];
            while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
            height[rank[i]]=k;      
        }
    }
    inline void getsa()
    {
        int *r=t1,*k=t2;
        fo(i,0,m)c[i]=0;
        fo(i,1,n)c[r[i]=s[i]]++;
        fo(i,1,m)c[i]+=c[i-1];
        fd(i,n,1)sa[c[r[i]]--]=i;
        for(int j=1;j<=n;j<<=1)
        {
            int p=0;
            for(int i=n-j+1;i<=n;i++)k[++p]=i;
            fo(i,1,n)if (sa[i]>j)k[++p]=sa[i]-j;

            for(int i=0;i<=m;i++) c[i]=0;
            for(int i=1;i<=n;i++) c[r[k[i]]]++;
            for(int i=1;i<=m;i++) c[i]+=c[i-1];
            for(int i=n;i>=1;i--) sa[c[r[k[i]]]--]=k[i];

            swap(r,k);p=0;r[sa[1]]=++p;
            for(int i=2;i<=n;i++) r[sa[i]]=cmp(k,sa[i],sa[i-1],j)?p:++p;
            if(p>=n) break;m=p;
        } 
    }
    inline int lcp(int x,int y)
    {
        x=rank[x],y=rank[y];
        if (x>y)swap(x,y);
        x++;
        int t=Log[y-x+1];
        return min(mn[x][t],mn[y-Pow[t]+1][t]);
    } 
    inline void ini()
    {
        getsa();
        getheight();
        getST(mn,height);
    }
}a,b;
int ans;
inline void solve(int L)
{
    for(int i=1;i+B+L<=n;i+=L)
    if (s[i]==s[i+B+L])
    {
        int r=a.lcp(i,i+B+L),l=b.lcp(n-i+2,n-i-B-L+2);
        l=min(l,L-1);r=min(r,L);
        if (l+r>=L)ans+=l+r+1-L;
    }
}
int main()
{
    n=read();
    B=read();
    fo(i,1,n)
    {
        s[i]=read();
        if (i!=1)mp[++tot]=s[i-1]=s[i]-s[i-1];
    }
    n--;
    sort(mp+1,mp+1+n);
    tot=0,mp[++tot]=mp[1];
    fo(i,2,n)if (mp[i]!=mp[i-1])mp[++tot]=mp[i];
    fo(i,1,n)s[i]=find(s[i]);
    m=n;

    iniST();
    a.ini();
    reverse(s+1,s+1+n);
    b.ini();
    reverse(s+1,s+1+n);
    for(int l=1;l+l+B<=n;l++)solve(l);
    printf("%d\n",ans);
}
### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值