【USACO06DEC/Luogu2852】牛奶模式 后缀数组 半模板题

原题走这里

本题实质上,就是求序列中最长的出现了至少 k k 次的子串
而子串实质上就是后缀的前缀,于是我们可以使用后缀数组
鉴于后缀数组中,拥有相同前缀的的一系列后缀会表现为一段连续的区间
而相邻后缀的公共前缀长度又储存在 height h e i g h t ,因此我们只需要求height数组中所有长度为 k1 k − 1 的区间的区间最小值的最大值即可。

虽然求区间最小值可以RMQ,然而偷懒的我决定维护单调队列,实际上也快不了多少,但是好写啊

具体实现见代码如下:

// luogu-judger-enable-o2
#include 
using namespace std;
int n,K,a[20010],tp[20010],SA[20010],r[20010],t[1000010],q[20010],h[20010],qh,qt,ret;
void Sort(int m)
{
    memset(t,0,sizeof(int)*(m+1));
    for(int i=1;i<=n;i++)
    {
        t[r[i]]++;
    }
    for(int i=0;i<=m;i++)
    {
        t[i]+=t[i-1];
    }
    for(int i=n;i;i--)
    {
        SA[t[r[tp[i]]]--]=tp[i]; 
    }
}
int main()
{
    cin>>n>>K;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        tp[i]=i;
        r[i]=a[i];
    }
    Sort(1000000);
    for(int i=1,m=1000000,p=0;p1,m=p)
    {
        p=0;
        for(int j=n-i+1;j<=n;j++)
        {
            tp[++p]=j;
        }
        for(int j=1;j<=n;j++)
        {
            if(SA[j]>i)
            {
                tp[++p]=SA[j]-i;
            }
        }
        Sort(m);
        swap(tp,r);
        r[SA[1]]=p=1;
        for(int j=2;j<=n;j++)
        {
            r[SA[j]]=((tp[SA[j]]==tp[SA[j-1]])&&(tp[SA[j]+i]==tp[SA[j-1]+i])?p:++p);
        }
    }
    for(int i=1,j,k=0;i<=n;h[r[i++]]=k)
    {
        for(k=(k?k-1:k),j=SA[r[i]-1];a[i+k]==a[j+k];k++);
    }
    for(int i=1;i<=n;i++)
    {
        while(qhq[qt-1]]>h[i])qt--;
        q[qt++]=i;
        if(q[qh]<=i-K+1)qh++;
        if(i>=K-1)ret=max(ret,h[q[qh]]);
    }
    cout<return 0;
}

你可能感兴趣的:(【USACO06DEC/Luogu2852】牛奶模式 后缀数组 半模板题)