2019牛客多校第四场——I.string【后缀自动机+回文自动机】

题目链接: string

题目描述

We call a,ba,ba,b non-equivalent if and only if a≠ba \neq ba​=b and a≠rev(b)a \neq rev(b)a​=rev(b), where rev(s)rev(s)rev(s) refers to the string obtained by reversing characters of sss, for example rev(abca)=acbarev(abca)=acbarev(abca)=acba.

There is a string sss consisted of lower-case letters. You need to find some substrings of sss so that any two of them are non-equivalent. Find out what's the largest number of substrings you can choose.

输入描述:

A line containing a string sss of lower-case letters.

输出描述:

A positive integer - the largest possible number of substrings of sss that are non-equivalent.

输入

abac

输出

8

说明

The set of following substrings is such a choice: abac,b,a,ab,aba,bac,ac,cabac,b,a,ab,aba,bac,ac,cabac,b,a,ab,aba,bac,ac,c.

备注:

1≤∣s∣≤2×1051 \leq |s|\leq 2 \times 10^51≤∣s∣≤2×105, sss is consisted of lower-case letters.

题意:

找出一个字符串的所有子串中 每个子串本身以及反转之后 两两之间各不相同的子串的个数 

分析:

通过pam得到所有子串中不同的回文串个数s1-2;(一个空串)

构建新的字符串 原串+(特殊符号)+反转串 通过sam得到不同的子串个数s2-2;(两个空串)

ans=(s1+s2-3)/2;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=8e5+7;
char s[maxn];
struct SAM{
    int next[maxn][27],fa[maxn],len[maxn];
    int root,tot,last; ll dp[maxn];
    void init(){
        tot=0;
        memset(dp,-1,sizeof(dp));
        last=root=newnode(0);
    }
    int newnode(int l){
        fa[tot]=-1;
        for(int i=0;i<27;++i)  next[tot][i]=-1;
        len[tot++]=l; return tot-1;
    }
    void insert(int x){
        int p=last; int cur=newnode(len[p]+1);
        while(p!=-1&&next[p][x]==-1){
            next[p][x]=cur; p=fa[p];
        }
        if(p==-1) fa[cur]=root;
        else{
            int q=next[p][x];
            if(len[q]==len[p]+1) fa[cur]=q;
            else{
                int tmp=newnode(len[p]+1);
                memcpy(next[tmp],next[q],sizeof(next[q]));
                fa[tmp]=fa[q]; fa[q]=fa[cur]=tmp;
                while(p!=-1&&next[p][x]==q){
                    next[p][x]=tmp; p=fa[p];
                }
            }
        }
        last=cur;
    }
    ll dfs(int u){
        if(dp[u]!=-1) return dp[u];
        ll res=1;
        for(int i=0;i<26;++i){
            if(next[u][i]==-1) continue;
            res+=dfs(next[u][i]);
        }
        return dp[u]=res;
    }
}sam;
struct PAM{
    int next[maxn][26],fail[maxn],len[maxn];
    int txt[maxn];
    int tot,root0,root1,last,size;
    void init(){
        last=tot=size=0; txt[size]=-1;
        root0=newnode(0); root1=newnode(-1);
        fail[root0]=1; fail[root1]=0;
    }
    int newnode(int l){
        len[tot]=l;
        memset(next[tot],0,sizeof(next[tot]));
        tot++; return tot-1;
    }
    int getfail(int x){
        while(txt[size-len[x]-1]!=txt[size]) x=fail[x];
        return x;
    }
    void insert(int c){
        txt[++size]=c; int now=getfail(last);
        if(!next[now][c]){
            int tmp=newnode(len[now]+2);
            fail[tmp]=next[getfail(fail[now])][c];
            next[now][c]=tmp;
        }
        last=next[now][c];
    }
}pam;
int main()
{
    scanf("%s",s);
    sam.init();pam.init();
    int st=strlen(s);
    for (int i=0;i<st;i++)
    {
        sam.insert(s[i]-'a');
        pam.insert(s[i]-'a');
    }
    sam.insert(26);
    for (int i=st-1;i>=0;i--) sam.insert(s[i]-'a');
    printf("%lld\n",(sam.dfs(sam.root)+pam.tot-2-1)/2);
    return 0;
}

 其实我就是来贴板子的 贴完了 我走了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值