BZOJ3620: 似乎在梦中见过的样子 解题报告

15 篇文章 0 订阅
4 篇文章 0 订阅

这题一直没想出来,后来发现 O(n2) 能过…….


SA+n2 枚举
枚举一个左端点i,处理一下前缀i和每个前缀的height值,然后往右枚举右端点j,判一下height值是不是>k,如果是的话,把不覆盖中间全部区间的所有方案加入答案,注意不同j的答案可能重复,所以把能成为答案的j的区间存到一个数组里,弄一下前缀和,扫完j扫一遍数组就好了(其实好像可以直接一个一个判不用这样)


code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 15100;
const int maxl = 15;
int sa[maxn],rank[maxn],t[maxn],height[maxn];
int fir[maxn],sec[maxn];
int n;
void sort_(int *ret,int *rk,int *str,int n,int m)
{
    for(int i=0;i<=m;i++)t[i]=0;
    for(int i=1;i<=n;i++)t[str[rk[i]]]++;
    for(int i=1;i<=m;i++)t[i]+=t[i-1];
    for(int i=n;i>=1;i--)ret[t[str[rk[i]]]--]=rk[i];
}
int str[maxn];
void get_sa(int n,int m)
{
    for(int i=1;i<=n;i++)rank[i]=i;
    sort_(sa,rank,str,n,m);
    rank[sa[1]]=1;
    for(int i=2;i<=n;i++)
        rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
    int cnt=rank[sa[n]],p=1;
    while(cnt!=n)
    {
        for(int i=1;i<=n;i++)
        {
            sa[i]=i;
            fir[i]=rank[i];
            sec[i]=i+p>n?0:rank[i+p];
        }
        sort_(rank,sa,sec,n,n);
        sort_(sa,rank,fir,n,n);
        rank[sa[1]]=1;
        for(int i=2;i<=n;i++)
            rank[sa[i]]=rank[sa[i-1]]+
            (fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]]);
        cnt=rank[sa[n]];p<<=1;
    }
}
void get_height()
{
    height[1]=0;
    int k=0;
    for(int i=1;i<=n;i++)
    {
        if(k)k--;
        if(rank[i]==1)continue;
        while(str[i+k]==str[sa[rank[i]-1]+k])k++;
        height[rank[i]]=k;
    }
}
char st[maxn];
int kn;
int R[maxl][maxn],L[maxn],s[maxn];

int main()
{
    scanf("%s",st); n=strlen(st);
    scanf("%d",&kn);
    for(int i=1;i<=n;i++)str[i]=st[i-1]-'a'+1;

    get_sa(n,28);
    get_height();
    /*
    for(int i=1;i<=n;i++)R[0][i]=height[i];
    for(int i=1;i<maxl;i++)
    {
        int kl=1<<(i-1);
        for(int j=1;j-1+(1<<i)<=n;j++)
            R[i][j]=min(R[i-1][j],R[i-1][j+kl]);
    }
    for(int )*/
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int l=0;
        int tt=rank[i],hi=height[tt];
        for(int j=tt-1;j>=1;j--)
        {
            L[sa[j]]=hi;
            if(height[j]<hi)hi=height[j];
        }
        hi=height[tt+1];
        for(int j=tt+1;j<=n;j++)
        {
            if(height[j]<hi)hi=height[j];
            L[sa[j]]=hi;
        }

        for(int j=i+2;j<=n;j++)
        {
            if(L[j]>=kn)
            {
                int tmp=L[j];
                if(i+tmp-1>=j-1)tmp=j-1-i;
                s[j+kn-1]++;s[j+tmp]--;
            }
        }
        int ts=0;
        for(int j=i+2;j<=n;j++)
        {
            ts+=s[j];
            s[j]=0;
            if(ts>0)ans++;
        }
    }
    printf("%lld\n",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值