uva10829 - L-Gap Substrings

    xiaoxiao2022-07-14  158

    任务量统计

    工作日五道题 ( 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; }
    最新回复(0)