zoukankan      html  css  js  c++  java
  • [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分

    题目描述

    如果一个字符串可以被拆分为 (AABB) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

    例如,对于字符串 (aabaabaa) ,如果令 (A=aab)(B=a) ,我们就找到了这个字符串拆分成 (AABB) 的一种方式。

    一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 (A=a)(B=baa) ,也可以用 (AABB) 表示出上述字符串;但是,字符串 (abaabaa) 就没有优秀的拆分。

    现在给出一个长度为 (n) 的字符串 (S) ,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

    以下事项需要注意:
    出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。

    在一个拆分中,允许出现 (A=B) 。例如 (cccc) 存在拆分 (A=B=c)

    字符串本身也是它的一个子串。

    输入输出格式

    输入格式:

    每个输入文件包含多组数据。

    输入的第一行只有一个整数 (T) ,表示数据的组数。保证 (1≤T≤10)

    接下来 (T) 行,每行包含一个仅由英文小写字母构成的字符串 (S) ,意义如题所述

    输出格式:

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

    输入输出样例

    输入样例#1:

    复制

    4

    aabbbb

    cccccc

    aabaabaabaa

    bbaabaababaaba

    输出样例#1:

    复制

    3
    5
    4
    7

    说明

    我们用 (S_{i,j}) 表示字符串 (S)(i) 个字符到第 (j) 个字符的子串(从 (1) 开始计数)。

    第一组数据中,共有 (3) 个子串存在优秀的拆分:

    (S_{1,4}=aabb) ,优秀的拆分为 $ A=a$ , (B=b)

    (S_{3,6}=bbbb) ,优秀的拆分为 (A=b)(B=b)

    (S_{1,6}=aabbbb) ,优秀的拆分为 (A=a)(B=bb)

    而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 (3)

    第二组数据中,有两类,总共 (4) 个子串存在优秀的拆分:

    对于子串 (S_{1,4}=S_{2,5}=S_{3,6}=cccc) ,它们优秀的拆分相同,均为 (A=c)(B=c) ,但由于这些子串位置不同,因此要计算 (3) 次;

    对于子串 (S_{1,6}=cccccc) ,它优秀的拆分有 (2) 种:(A=c)(B=cc)(A=cc)(B=c) ,它们是相同子串的不同拆分,也都要计入答案。

    所以第二组数据的答案是 (3+2=5)

    第三组数据中, $S_{1,8} $ 和 (S_{4,11}) 各有 (2) 种优秀的拆分,其中 (S_{1,8}) 是问题描述中的例子,所以答案是 (2+2=4) .

    第四组数据中, (S_{1,4},S_{6,11},S_{7,12},S_{2,11},S_{1,8}) 各有 (1) 种优秀的拆分, (S_{3,14})(2) 种优秀的拆分,所以答案是 (5+2=7)

    对于全部的测试点,保证 (1≤T≤10) 。以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 (T) 组数据均满足限制条件。

    我们假定 (n) 为字符串 (S) 的长度,每个测试点的详细数据范围见下表:

    题解

    先推荐一下这一个博客,可以说是写的非常清楚了

    好了,现在我来说一说自己的理解。
    先写一手hash吧,95分,可以说是非常良心了,我们只需要预先处理好这个字符串的hash值,然后一个循环枚举起点,一个循环枚举长度,然后记录一下AA(注意BB就不用再管了)的(l[i])(r[i])相乘即可。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=2001;
    const ll mod=(1<<31)-1;
    const int base=131;
    ll addx[N],hah[N],n,T;
    char s[N+1];
    void solve(){
        ll ans=0;
        scanf("%s",s+1);
        n=strlen(s+1);
        addx[0]=1;
        for(int i=1;i<=n;i++)addx[i]=addx[i-1]*base%mod;
        for(int i=1;i<=n;i++)hah[i]=(hah[i-1]*base+s[i])%mod;
        for(int i=1;i<=n;i++)
        {
            ll l=0,r=0;
            for(int j=1;j<=n;j++){
                if(i-2*j>=0){
                    ll x=((hah[i]-hah[i-j]*addx[j])%mod+mod)%mod;
                    ll y=((hah[i-j]-hah[i-j*2]*addx[j])%mod+mod)%mod;
                    if(x==y)l++;
                }
                if(i+2*j<=n){
                    ll x=((hah[i+j]-hah[i]*addx[j])%mod+mod)%mod;
                    ll y=((hah[i+2*j]-hah[i+j]*addx[j])%mod+mod)%mod;
                    if(x==y)r++;
                }
            }
            ans+=l*r;
        }
        printf("%lld
    ",ans);
    }
    
    int main(){
        cin>>T;
        while(T--)
        solve();
        return 0;
    }
    



    好了,接下来讲一讲满分做法。
    推了我蛮久(最后还是翻题解了)
    AA相同,即有子串是相同且靠近的,这让我们想到什么?
    后缀数组维护(lcp)
    那么一个简单的结论变出来了。
    每次枚举长度(len)(j-i=len)
    AA串一共有(lcp-len+1)个。
    这个比较好手推。
    枚举长度(1-n),在枚举起点和终点每次跳(len)
    然后时间复杂度是调和级数的。即(O(nlogn))
    但是实际上我们是有遗漏的情况的。
    因为起点和终点是每次跳(len)的,且只求的后缀的部分。
    求图证翻上面的博客。这道题我承认有点讲不清。
    那么如果有以起点为末尾的前缀呢与终点匹配呢?
    那么我们也要求出(lcs)即最长公共后缀。
    AA的贡献为(lcs+lcp>=len)
    但是这样又又又有问题了
    我们会重复计算别的子串。
    怎么办?我们限制长度。就是说,这个区间算过就直接跳过。
    换句话说,就是超出本次枚举长度以外的贡献忽略??
    我感觉我有点讲不清了qwq,还是95分好拿,何必zuosi去AC呢。
    ps:最后一个点还要开long long




    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #define ll long long
    using namespace std;
    const int N=30001;
    int T,n,m;
    ll l[N],r[N],lg[N];
    char ss[N];
    struct node{
        int st[21][N];
        char s[N];
        int tp[N],rak[N],sa[N],num[N],H[N];
        void clear(){
            memset(num,0,sizeof(num));
            memset(rak,0,sizeof(rak));
            memset(H,0,sizeof(H));
            memset(sa,0,sizeof(sa));
            memset(tp,0,sizeof(tp));
        }
        void Sort(){
            for(int i=0;i<=m;i++)num[i]=0;
            for(int i=1;i<=n;i++)num[rak[i]]++;
            for(int i=1;i<=m;i++)num[i]+=num[i-1];
            for(int i=n;i>=1;i--)sa[num[rak[tp[i]]]--]=tp[i];
        }
        void SA_sort(){
            m=10001;
            for(int i=1;i<=n;i++)tp[i]=i,rak[i]=s[i]-'a'+1;Sort();
            int cnt=0,p=0,w=1;
            while(p<n){
                for(int i=1;i<=w;i++)tp[++cnt]=n-w+i;
                for(int i=1;i<=n;i++)if(sa[i]>w)tp[++cnt]=sa[i]-w;
                Sort();swap(tp,rak);rak[sa[1]]=p=1;
                for(int i=2;i<=n;i++)
                if(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])
                rak[sa[i]]=p;else rak[sa[i]]=++p;
                w<<=1;m=p;cnt=0;
            }
            for(int i=1;i<=n;i++)rak[sa[i]]=i;
            int k=0;
            for(int i=1;i<=n;i++){
                if(k)k--;
                while(s[i+k]==s[sa[rak[i]-1]+k]&&i+k<=n&&sa[rak[i]-1]+k<=n)k++;
                H[rak[i]]=k;
            }
            memset(st,63,sizeof(st));
            for(int i=1;i<=n;i++)
            st[0][i]=H[i];
            for(int j=1;j<=15;j++)
                for(int i=1;i<=n;i++)
                st[j][i]=min(st[j-1][i],st[j-1][i+(1<<(j-1))]);
        }
        int query(int i,int j){
            int l=rak[i],r=rak[j];if(l>r)swap(l,r);l++;
            return min(st[lg[r-l+1]][l],st[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);
        }
    }s1,s2;
    
    void solve(){
        for(int i=2;i<=30000;++i)lg[i]=lg[i>>1]+1;
        s1.clear();s2.clear();
        scanf("%s",ss+1);
        n=strlen(ss+1);
        for(int i=1;i<=n;i++)s1.s[i]=ss[i];
        for(int i=1;i<=n;i++)s2.s[n-i+1]=ss[i];
        s1.SA_sort();
        s2.SA_sort();
        for(int i=1;i<=n;i++)l[i]=r[i]=0;
        for(int k=1;k<=n/2;k++)
            for(int i=k,j=i+k;j<=n;j+=k,i+=k){
                int x=min(k,s1.query(i,j));
                int y=min(k-1,s2.query(n-i+2,n-j+2));
                int en=x+y-k+1;
                if(x+y>=k){
                    r[i-y]++;r[i-y+en]--;
                    l[j+x-en]++;l[j+x]--;
                }
            }
        for(int i=1;i<=n;i++)
        l[i]+=l[i-1],r[i]+=r[i-1];
        ll ans=0;
        for(int i=1;i<n;i++)
        ans+=1ll*l[i]*r[i+1];
        printf("%lld
    ",ans);
    }
    
    int main(){
        cin>>T;             
        while(T--)
        solve();
        return 0;
    }
    
  • 相关阅读:
    百度之星资格赛1001——找规律——大搬家
    HDU1025——LIS——Constructing Roads In JGShining's Kingdom
    DP(递归打印路径) UVA 662 Fast Food
    递推DP UVA 607 Scheduling Lectures
    递推DP UVA 590 Always on the run
    递推DP UVA 473 Raucous Rockers
    博弈 HDOJ 4371 Alice and Bob
    DFS(深度) hihoCoder挑战赛14 B 赛车
    Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2)
    DP(DAG) UVA 437 The Tower of Babylon
  • 原文地址:https://www.cnblogs.com/hhh1109/p/9475129.html
Copyright © 2011-2022 走看看