ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P1117 [NOI2016] 优秀的拆分

2021-06-28 23:04:02  阅读:169  来源: 互联网

标签:AA P1117 int len -- NOI2016 maxn 拆分 define


【题意】

能被表示为AABB的形式被称为一种优秀的拆分,求一个字符串有多少个不同的优秀的拆分

注意本质相同的子串在不同位置要重复计算

【分析】

首先我们不难想到计算f[i]表示i为结尾的AA形式的个数,g[i]表示i开头的AA形式的个数

答案就为f[i]*g[i+1] i=1-(n-1)

问题就变成了如何快速的求出f和g

我们可以通过哈希的方式$O(n^2)$暴力算出每个位置的f和g,这样做可以得到95分的好成绩

那么谁还在乎正解啊

【95pts代码】

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define fi first
#define se second
#define lson now<<1
#define rson now<<1|1
typedef long long ll;
const int maxn=2005;
ll f[maxn],g[maxn];
char s[maxn];
const int mod=1e9+7;
const int base=2333;
int h[maxn],len,p[maxn];
void init()
{
    for(int i=1;i<=len;i++)
        h[i]=(1LL*h[i-1]*base+s[i]-'a')%mod;
}
int get(int l,int r)
{
    return (h[r]-1LL*h[l-1]*p[r-l+1]%mod+mod)%mod;
}
int main()
{
    int t;
    scanf("%d",&t);
    p[0]=1;
    for(int i=1;i<=2005;i++) p[i]=1LL*p[i-1]*base%mod;
    while(t--)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        for(int i=1;i<=len;i++) f[i]=g[i]=0;
        init();
        for(int i=2;i<=len;i++)
            for(int j=1;j<=(i/2);j++)
                if(get(i-2*j+1,i-j)==get(i-j+1,i))
                    f[i]++;
        for(int i=1;i<len;i++)
            for(int j=1;j<=(len-i+1)/2;j++)
                if(get(i,i+j-1)==get(i+j,i+2*j-1))
                    g[i]++;
        ll ans=0;
        for(int i=1;i<len;i++) ans+=1LL*f[i]*g[i+1];
        printf("%lld\n",ans);
    }
    return 0;
}

 

正解的方法就很神仙了,不知道怎么想到建立关键点

枚举AA中A的长度len,然后把整个字符串中长度为len的倍数的位置设立成关键点

这样每个AA一定会经过两个关键点,设这两个关键点为i,j,满足j=i+len

我们计算出lcp=lcp(suf(i),suf(j)),lcs=lcs(pre(i),pre(j))

简单画一下图就可以发现,当i-j中间的lcp和lcs没有交点就是存在AA

而重叠的部分就是AA串可以挪动的长度

我们利用这一点就可以打一下差分标记计算f,g了

时间复杂度$O(nlogn)$由于后缀数组和调和级数都是一只log

【参考代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mp make_pair
#define fi first
#define se second
const int maxn=3e4+5;
char a[maxn];
int n;
int lg[maxn];
struct SA
{
    int sa[maxn],rk[maxn],oldrk[maxn];
    int fz[maxn],cnt[maxn],id[maxn];
    int h[maxn];
    bool cmp(int x,int y,int w)
    {
        return  oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[w+y];
    }
    void build()
    {
        int m=233;
        for(int i=0;i<=m;i++) cnt[i]=0;
        for(int i=1;i<=n;i++) cnt[rk[i]=a[i]]++;
        for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
        int i,p;
        for(int w=1;;w<<=1,m=p)
        {
            for(p=0,i=n;i>n-w;i--) id[++p]=i;
            for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w;
            for(i=0;i<=m;i++) cnt[i]=0;
            for(i=1;i<=n;i++) cnt[fz[i]=rk[id[i]]]++;
            for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for(i=n;i>=1;i--) sa[cnt[fz[i]]--]=id[i];
            for(i=1;i<=n;i++) oldrk[i]=rk[i];
            for(p=0,i=1;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
            if(n==p)
            {
                for(i=1;i<=n;i++) sa[rk[i]]=i;
                break;
            }
        }
        int k=0;
        for(int i=1;i<=n;i++)
        {
            if(k) k--;
            while(a[i+k]==a[sa[rk[i]-1]+k]) k++;
            h[rk[i]]=k;
        }       
    }
    int st[maxn][17];
    void ST_init()
    {
        memset(st,63,sizeof(st));
        for(int i=1;i<=n;i++) st[i][0]=h[i];
        for(int i=1;i<=16;i++)
            for(int j=1;j<=n;j++)
                st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
        
        // memset(st, 63, sizeof(st)); 
        // for (int i = 1; i <= n; i++) st[i][0]= h[i],printf("%d ",h[i]);; 
        // for (int j = 1; j <= 15; j++)
        //     for (int i = 1; i <= n; i++)
        //         st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]); 
       
    }
    int query(int l,int r)
    {
        int ll=min(rk[l],rk[r])+1,rr=max(rk[l],rk[r]);
        int i=lg[rr-ll+1];
        return min(st[ll][i],st[rr-(1<<i)+1][i]);
    }
}A,B;
int f[maxn],g[maxn];
int main()
{
    // freopen("a.in","r",stdin);
    // freopen("a.out","w",stdout);
    lg[0]=-1;
    for(int i=1;i<maxn;i++) lg[i]=lg[i>>1]+1;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",a+1);
        n=strlen(a+1);
        A.build(); A.ST_init();
        reverse(a+1,a+n+1);
        B.build(); B.ST_init();
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(int len=1;len<=n/2;len++)
            for(int i=len,j=i+len;j<=n;j+=len,i+=len)
            {
                int lcp=min(A.query(i,j),len),lcs=min(B.query(n-i +2,n-j+2),len-1); 
                // printf("%d %d\n",A.query(i,j),B.query(n-i +2,n-j+2));
                int t=lcs+lcp-len+1;
                if(lcs+lcp>=len)
                {
                    g[i-lcs]++; g[i-lcs+t]--;
                    f[j+lcp-t]++; f[j+lcp]--;
                }
            }
        for(int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];
        ll ans=0;
        // for(int i=1;i<=n;i++) printf("%d ",A.h[i]);
        // printf("\n");
        // for(int i=1;i<=n;i++) printf("%d ",B.h[i]);
        // printf("\n");        
        for(int i=1;i<n;i++) ans+=1ll*f[i]*g[i+1];
        printf("%lld\n",ans);
    }
    return 0;
}

 

标签:AA,P1117,int,len,--,NOI2016,maxn,拆分,define
来源: https://www.cnblogs.com/andylnx/p/14947861.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有