AmazingCounters.com

[BZOJ]4650: [Noi2016]优秀的拆分

Time Limit: 30 Sec  Memory Limit: 512 MB

Description

  如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。例如,对于字符串 aabaabaa,如果令 A=aabA=aab,B=aB=a,我们就找到了这个字符串拆分成 AABBAABB 的一种方式。一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=aA=a,B=baaB=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。现在给出一个长度为 nn 的字符串 SS,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。以下事项需要注意:出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。在一个拆分中,允许出现 A=BA=B。例如 cccc 存在拆分 A=B=cA=B=c。字符串本身也是它的一个子串。

Input

  每个输入文件包含多组数据。输入文件的第一行只有一个整数 TT,表示数据的组数。保证 1≤T≤101≤T≤10。接下来 TT 行,每行包含一个仅由英文小写字母构成的字符串 SS,意义如题所述。

Output

  输出 TT 行,每行包含一个整数,表示字符串 SS 所有子串的所有拆分中,总共有多少个是优秀的拆分。

Sample Input

  4
  aabbbb
  cccccc
  aabaabaabaa
  bbaabaababaaba

Sample Output

  3
  5
  4
  7
  我们用 S[i,j]S[i,j] 表示字符串 SS 第 ii 个字符到第 jj 个字符的子串(从 11 开始计数)。第一组数据中,共有 33 个子串存在优秀的拆分:S[1,4]=aabbS[1,4]=aabb,优秀的拆分为 A=aA=a,B=bB=b;S[3,6]=bbbbS[3,6]=bbbb,优秀的拆分为 A=bA=b,B=bB=b;S[1,6]=aabbbbS[1,6]=aabbbb,优秀的拆分为 A=aA=a,B=bbB=bb。而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 33。第二组数据中,有两类,总共 44 个子串存在优秀的拆分:对于子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它们优秀的拆分相同,均为 A=cA=c,B=cB=c,但由于这些子串位置不同,因此要计算 33 次;对于子串 S[1,6]=ccccccS[1,6]=cccccc,它优秀的拆分有 22 种:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它们是相同子串的不同拆分,也都要计入答案。所以第二组数据的答案是 3+2=53+2=5。第三组数据中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 种优秀的拆分,其中 S[1,8]S[1,8] 是问题描述中的例子,所以答案是 2+2=42+2=4。第四组数据中,S[1,4]S[1,4],S[6,11]S[6,11],S[7,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 种优秀的拆分,S[3,14]S[3,14] 有 22 种优秀的拆分,所以答案是 5+2=75+2=7。

Solution

  只要统计每个位置为左/右端点时AA的个数即可统计答案。枚举A的长度$l$,那么我们取$n/l$个相邻间隔为$l$的代表点,任意一个合法的AA必然同时包含两个相邻的代表点,且包含两个相邻代表点的合法AA的左右端点都是连续的区间,我们求出这两个代表点的最长公共前后缀即可求出区间,打上差分标记即可。枚举这些代表点的复杂度是$O(nlogn)$,用后缀数组可以$O(1)$求最长公共前后缀,总复杂度$O(nlogn)$。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MN 60000
#define K 16
#define ms(a) memset(a,0,sizeof(a))
char s[MN+5];
int v[MN+5],A[MN*4+20],*sa=A,*rk=sa+MN+5,*nsa=rk+MN+5,*nrk=nsa+MN+5;
int len,h[K][MN+5],lg[MN+5],L[MN+5],R[MN+5];
int query(int l,int r)
{
    int x=lg[r-l+1];
    return min(h[x][l],h[x][r-(1<<x)+1]);
}
int lcp(int x,int y)
{
    if(rk[x]>rk[y])swap(x,y);
    return query(rk[x]+1,rk[y]);
}
int lcs(int x,int y)
{
    return lcp(2*len+2-x,2*len+2-y);
}
int main()
{
    int T,n,i,j,l;long long ans;
    for(i=3;i<=MN;++i)lg[i]=lg[i+1>>1]+1;
    scanf("%d",&T);
    while(T--)
    {
        ms(s);ms(v);ms(A);ms(L);ms(R);
        scanf("%s",s+1);len=strlen(s+1);
        s[len+1]='a'-1;n=len*2+1;
        for(i=1;i<=len;++i)s[i+len+1]=s[len-i+1];
        for(i=1;i<=n;++i)++v[s[i]];
        for(i='a';i<='z';++i)v[i]+=v[i-1];
        for(i=1;i<=n;++i)sa[v[s[i]]--]=i;
        for(i=1;i<=n;++i)rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
        for(l=1;l<n;l<<=1,swap(sa,nsa),swap(rk,nrk))
        {
            for(i=1;i<=n;++i)v[rk[sa[i]]]=i;
            for(i=n;i;--i)if(sa[i]>l)nsa[v[rk[sa[i]-l]]--]=sa[i]-l;
            for(i=0;i<l;++i)nsa[v[rk[n-i]]--]=n-i;
            for(i=1;i<=n;++i)nrk[nsa[i]]=nrk[nsa[i-1]]+(rk[nsa[i]]!=rk[nsa[i-1]]||rk[nsa[i]+l]!=rk[nsa[i-1]+l]);
        }
        for(i=1,l=0;i<=n;++i,l?--l:0)
            if(rk[i]>1){for(j=sa[rk[i]-1];s[i+l]==s[j+l];++l);h[0][rk[i]]=l;}
        for(i=1;i<K;++i)for(j=1;j+(1<<i)-1<=n;++j)h[i][j]=min(h[i-1][j],h[i-1][j+(1<<i-1)]);
        for(i=1;2*i<len;++i)for(j=1;j+i<=len;j+=i)
        {
            int a=min(lcs(j,j+i),i),b=min(lcp(j,j+i),i);
            if(a+b>i)++L[j-a+1],--L[j+b-i+1],++R[j+i-a+i],--R[j+i+b];
        }
        for(ans=0,i=1;i<len;++i)ans+=1LL*(R[i]+=R[i-1])*(L[i+1]+=L[i]);
        printf("%lld\n",ans);
    }
}

 

posted on 2017-06-03 17:16  ditoly  阅读(257)  评论(0编辑  收藏  举报