uva10829 - L-Gap Substrings

任务量统计

工作日五道题 ( 5 / 5 ) (5/5) (5/5)

链接

https://cn.vjudge.net/problem/UVA-10829

题解

这题有点神啊
对于形如 A B A ABA ABA的串,我枚举 A A A的长度 u u u,然后把整个字符串按照 u u u分块,然后谜之乱搞…
我觉得按照 u u u分块的意义在于:我想要统计长度恰好为 u u u的满足条件的子串 A A A的个数,分块之后就带来一个便利,子串 A A A的左端点和右端点必然在相邻的两个块中(假设在同一个块中,那么区间长度小于 u u u;假设在不同的块中,那么区间长度大于 u u u
那么分块之后我就有一个很显然的思路,在每个块中枚举左端点 l l l,再根据我枚举的 u u u确定串 A A A的位置,然后每个字符下标加上 u + g u+g u+g就得到了后面待匹配串的位置,进行暴力匹配从而统计答案
考虑怎么优化:既然每次比较的两个字符的坐标之差为 u + g u+g u+g,那么我是否可以先预处理一下对应位置的字符是不是相等?
如果相等我就放一个 1 1 1,不相等我就放一个 0 0 0,那么其实是统计左端点在我所枚举的块内,长度为 u u u的连续 1 1 1区间的个数。
假如我把每个长度为 u u u的块的范围设为左闭右开,可以发现所有的区间都包含了块的最后一个点(编号为 i ∗ u − 1 i*u-1 iu1),既然要统计包含这个点的全 1 1 1区间,我可以从这个点往前统计连续 1 1 1的个数 l 1 l1 l1,往后统计连续 1 1 1的个数 l 2 l2 l2,那么这个块对答案的贡献就是 m a x ( 0 , l 1 + l 2 − l + 1 ) max(0,l1+l2-l+1) max(0,l1+l2l+1)
后缀数组加速一下,每次查询 O ( 1 ) O(1) O(1),总复杂度是 O ( n ln ⁡ ( n ) ) O(n\ln(n)) O(nln(n))

总结

遇到一个字符串题,不一定先往算法上套。也许可以先想暴力怎么做,然后再用数据结构或者算法来优化。当然也不是绝对的,到底是先想数据结构/算法,还是先想怎么暴力,这二者还是要靠直觉和经验进行搭配。

一种口胡做法

这题可以等价于求 ∣ s a x − s a y ∣ ≤ l c p ( x , y ) + g |sa_x-sa_y|\leq lcp(x,y)+g saxsaylcp(x,y)+g的点对 ( x , y ) (x,y) (x,y)的个数
l c p lcp lcp实际上就是一堆 h e i g h t height height m i n min min,我先预处理出来每个 h e i g h t height height作为 m i n min min的有效区间,然后这个 h e i g h t height height就可以把有效区间分成两半,假设这个 h e i g h t height height t t t,那么就是找 ∣ s a x − s a y ∣ ≤ t + g |sa_x-sa_y| \leq t+g saxsayt+g ( x , y ) (x,y) (x,y)的个数。这两段区间里肯定有一个较小的段,我先在这个区间枚举 i i i,然后用主席树到对应的另一半区间里去找符合条件的解,复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
(可能会写,也可能就这样扔着qwq)

代码

#include <bits/stdc++.h>
#define maxn 50010
#define maxk 17
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct SuffixArray
{
    int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], st[maxk+2][maxn], N;
    bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
    void clear()
    {
        cl(sa), cl(rank), cl(ws), cl(wv), cl(wa), cl(wb), cl(height), cl(st);
    }
    void build(int *r, int n, int m)
    {
        N=n;
        n++;
        int i, j, k=0, p, *x=wa, *y=wb, *t;
        for(i=0;i<m;i++)ws[i]=0;
        for(i=0;i<n;i++)ws[x[i]=r[i]]++;
        for(i=1;i<m;i++)ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
        for(p=j=1;p<n;j<<=1,m=p)
        {
            for(p=0,i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<n;i++)wv[i]=x[y[i]];
            for(i=0;i<m;i++)ws[i]=0;
            for(i=0;i<n;i++)ws[wv[i]]++;
            for(i=1;i<m;i++)ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
        
        for(i=0;i<n;i++)rank[sa[i]]=i;
        
        for(i=0;i<n-1;height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    }
    void build_st()     //st表
    {
        int i, k;
        for(i=1;i<=N;i++)st[0][i]=height[i];
        for(k=1;k<=maxk;k++)
            for(i=1;i+(1<<k)-1<=N;i++)
                st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
    }
    int lcp(int x, int y)   //最长公共前缀
    {
        int l=rank[x], r=rank[y];
        if(l>r)swap(l,r);
        if(l==r)return N-sa[l];
        int t=log2(r-l);
        return min(st[t][l+1],st[t][r-(1<<t)+1]);
    }
}SA1, SA2;
int read(int x=0)
{
    int c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return f*x;
}
char s[maxn];
int r[maxn];
int main()
{
    int T=read(), len, g, i, L, R, mid, kase(0), u;
    ll ans;
    while(T--)
    {
        g=read();
        scanf("%s",s);
        len=strlen(s);
        
        for(i=0;i<len;i++)r[i]=s[i]-'a'+1;
        r[len]=0;
        SA1.clear();
        SA1.build(r,len,30);
        SA1.build_st();

        reverse(r,r+len);
        r[len]=0;
        SA2.clear();
        SA2.build(r,len,30);
        SA2.build_st();

        ans=0ll;
        for(u=1;u<=len;u++)
        {
            for(i=u-1;i<len;i+=u)
            {
                int l1, l2;
                if(i+1+u+g>=len)l2=0;
                else l2=min(u-1,SA1.lcp(i+1,i+1+g+u));
                if(i+u+g>=len)break;
                else l1=min(u,SA2.lcp(len-(i+u+g)-1,len-i-1));
                ans+=max(0,l1+l2-u+1);
            }
        }
        printf("Case %d: %lld\n",++kase,ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值