2019牛客多校第四场 string

在这里插入图片描述

题意

定义 r e v ( S ) rev(S) rev(S) 为串 S S S 的颠倒,如 r e v ( a b a c ) = c a b a rev(abac)=caba rev(abac)=caba
现给出一个串,在他的所有子串中,任意两个串 a , b a,b a,b,不能出现 a = b 或 a = r e v ( b ) a=b或 a=rev(b) a=ba=rev(b) 的情况,求集合的最大值

题解

将原串的本质不同的子串分为3种,第一种是回文串,第二种是满足 s = r e v ( s ) s=rev(s) s=rev(s) 的,第三种是剩余的,设其数量分别为 x 、 y 、 z x、y、z xyz
将原串颠倒后拼接在其后面,再求一次子串个数,发现,第一种为 x x x,第二种为 y y y,第三种为 2 ⋅ z 2 \cdot z 2z
而我们的目的是想去掉 y y y 中的一半,发现,在加上一个 x x x 后,除以 2 2 2 就是答案
所以要求原串中本质不同的回文串的个数 x x x

代码
#include<bits/stdc++.h>
#define N 400010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef pair<int,int> pp;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;

char s[N];
int sa[N],t[N],t2[N],c[N],n,rak[N],height[N];
 
void build_sa(int m,char *s)
{
    int i,*x=t,*y=t2;
    for (i=0;i<m;i++)c[i]=0;
    for (i=0;i<n;i++)c[x[i]=s[i]]++;
    for (i=1;i<m;i++)c[i]+=c[i-1];
    for (i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for (int k=1;k<=n;k<<=1)
    {
        int p=0;
        for (i=n-k;i<n;i++)y[p++]=i;
        for (i=0;i<n;i++)if (sa[i]>=k)y[p++]=sa[i]-k;
        for (i=0;i<m;i++)c[i]=0;
        for (i=0;i<n;i++)c[x[y[i]]]++;
        for (i=0;i<m;i++)c[i]+=c[i-1];
        for (i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1; x[sa[0]]=0;
        for (i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if (p>=n) break;
        m=p;
    }
}
 
void getheight()
{
    int i,j,k=0;
    for (i=0;i<n;i++)rak[sa[i]]=i;
    for (i=0;i<n;i++){
        if (k)k--;
        if (!rak[i])continue;
        j=sa[rak[i]-1];
        while (s[i+k]==s[j+k])k++;
        height[rak[i]]=k;
    }
}

gp_hash_table<LL,bool>mp;
int str_t,slen,cnt,r[N];
LL p1[N/2],p2[N/2],h1[N/2],h2[N/2];
pp v[N];
char ss[N];
int Init(char *s){
    int len=strlen(s+1);
    ss[0]='$';ss[1]='#';
    int j=2;
    for (int i=1;i<=len;i++)ss[j++]=s[i],ss[j++]='#';
    ss[j]='\0';
    return j;
}
void Manacher(char *s){
    int len=Init(s);
    int p,mx=0;
    for (int i=1;i<len;i++)    {
        if (i<mx) r[i]=min(r[2*p-i],mx-i);else r[i]=1;
        while (ss[i-r[i]]==ss[i+r[i]]) r[i]++;
        if (mx<i+r[i])p=i,mx=i+r[i];
    }
    str_t=0;
    for (int i=2;i<len;i++)    {
        if (ss[i]=='#' && r[i]==1) continue;
        int x=i/2-r[i]/2+1,y=i/2+r[i]/2-!(i&1);
        v[str_t++]=pp(x,y);
    }
}
inline LL cal(LL *h,LL *hh,int x,int y){
LL t1=(h[y]-h[x-1]*p1[y-x+1]%mod+mod)%mod,
   t2=(hh[y]-hh[x-1]*p2[y-x+1]%P+P)%P;
    return t1<<30|t2;
}
int goooo(char *s){
    Manacher(s);
    for (int i=0;i<str_t;i++){
        int l=v[i].fi,r=v[i].se;
        while(l<=r){
            LL tm=cal(h1,h2,l,r);
            int t=mp[tm];
            if (t) break;
            mp[tm]=1;
            cnt++;
            l++,r--;
        }
    }
    return cnt;
}

int main()
{
    p1[0]=p2[0]=1;
    for(int i=1;i<N/2;i++) p1[i]=p1[i-1]*1331%mod,p2[i]=p2[i-1]*1331%P;
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;i++)
        h1[i]=(h1[i-1]*1331+s[i])%mod,h2[i]=(h2[i-1]*1331+s[i])%P;
    int ans1=goooo(s);
    
    for(int i=0;i<n;i++) s[i]=s[i+1];s[n]='0';int m=n;
    for(int i=1;i<=n;i++) s[m+i]=s[m-i];   
    n=n*2+2; s[n-1]='$'; s[n]=0;
    build_sa(200,s);
    getheight();
    LL ans2=0;
    for(int i=2;i<=n;i++){
        int tm,tt;
        if (sa[i]>m) tm=n-1-sa[i];else tm=m-sa[i];
        if (sa[i-1]>m) tt=n-1-sa[i-1];else tt=m-sa[i-1];
        ans2+=tt-min(tm,min(tt,height[i]));
    }   

    cout<<(ans1+ans2)/2<<endl;
  
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值