L-Gap Substrings(uva 10829)

题意:有一种形如uvu形式的字符串,其中u是非空字符串,且V的长度正好为L,那么称这个字符串为L-Gap字符串
给出一个字符串S,以及一个正整数L,问S中有多少个L-Gap子串. 

/*
    这道题用到一个常用的技巧(虽然我这是第一次见)。
    枚举U的长度,然后每隔U枚举i,然后确定j,则j一定在另一个U里面,有了i、j之后,向左向右扩展,看看能匹配多长。 
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 600010
using namespace std;
int t1[N],t2[N],c[N],s[N],sa[N],rk[N],height[N],n;
int st[N][21],lg2[N];
char ch[N];
bool cmp(int *y,int a,int b,int k){
    int a1=y[a],b1=y[b];
    int a2=a+k<=n?y[a+k]:-1;
    int b2=b+k<=n?y[b+k]:-1;
    return a1==b1&&a2==b2;
}
void DA(){
    int *x=t1,*y=t2,m=27;
    for(int i=1;i<=m;i++) c[i]=0;
    for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
    for(int i=2;i<=m;i++) c[i]+=c[i-1];
    for(int i=n;i;i--) sa[c[x[i]]--]=i;
    for(int k=1,p=0;k<=n;k*=2,m=p,p=0){
        for(int i=n-k+1;i<=n;i++) y[++p]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[x[y[i]]]++;
        for(int i=2;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
        swap(x,y);p=1;x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            if(cmp(y,sa[i-1],sa[i],k)) x[sa[i]]=p;
            else x[sa[i]]=++p;
        if(p>=n) break;
    }
}
void geth(){
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
    for(int i=1,j=0;i<=n;i++){
        while(s[i+j]==s[sa[rk[i]-1]+j]) j++;
        height[rk[i]]=j;
        if(j) j--;
    }
    for(int i=2;i<=n;i++) lg2[i]=lg2[i>>1]+1;
    for(int i=1;i<=n;i++) st[i][0]=height[i];
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int query(int l,int r){
    l=rk[l];r=rk[r];
    if(l>r) swap(l,r);
    int k=lg2[r-l];
    return min(st[l+1][k],st[r-(1<<k)+1][k]);
}
int main(){
    int T;scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        long long ans=0;int G;
        scanf("%d%s",&G,ch+1);
        int len=strlen(ch+1);
        for(int i=1;i<=len;i++) s[i]=s[len*2-i+2]=ch[i]-'a'+1;
        s[len+1]=27;n=len*2+1;
        DA();geth();
        for(int U=1;U*2+G<=len;U++)
            for(int i=1;i+U+G<=len;i+=U){
                int j=i+U+G;
                if(s[i]!=s[j]) continue;
                int L=min(U,query(i,j))+min(U,query(len*2-i+2,len*2-j+2))-1;//分别为向右和向左扩展 
                if(L>=U) ans+=L-U+1;
            }
        cout<<"Case "<<cas<<": "<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/harden/p/6597077.html