2019牛客多校竞赛第四场I题解

链接:https://ac.nowcoder.com/acm/contest/884/I
来源:牛客网

string
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
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.

示例1
输入
复制

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.


思路:将给定的字符串变为逆串然后加入到给定字符串的结尾,两个串中间用一个不会出现的字符隔开(26),然后很容易想到对这个字符串找到它的所有不一样的子串种类数除以二就是答案,但是这种思想是错的,因为有回文串的存在,回文串在这个连接而成的字符串中只会被统计一次种类,所以我们的做法还要求出这个串中有几种不同的回文子串,加上统计出来的不同子串数再除以二才是答案。
求字符串不同种类子串数量可以用后缀自动机跑dfs求出结果,而不同种类的回文子串可以套用回文自动机的板子,所以说这题是一个板子题也不为过=。=

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n); 
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define m(a,b) memset(a,b,sizeof a)
#define pb push_back 
#define lson id<<1
#define rson id<<1|1
typedef long long ll;
using namespace std;
const int maxn = 2e6+50,modd = 1e9 + 7,inf = 0x3f3f3f3f,INF = 0x7fffffff;
inline ll min(ll a,ll b){return a < b ? a : b;}
inline ll max(ll a,ll b){return a > b ? a : b;}
inline ll gcd(ll a,ll b){ return b==0? a: gcd(b,a%b); }
inline ll exgcd(ll a,ll b,ll &x,ll &y){ ll d; (b==0? (x=1,y=0,d=a): (d=exgcd(b,a%b,y,x),y-=a/b*x)); return d; }
inline ll qpow(ll a,ll n){ll sum=1;while(n){if(n&1)sum=sum*a%modd;a=a*a%modd;n>>=1;}return sum;}
inline ll qmul(ll a,ll n){ll sum=0;while(n){if(n&1)sum=(sum+a)%modd;a=(a+a)%modd;n>>=1;}return sum;}
inline ll inv(ll a) {return qpow(a,modd-2);}//求逆元 
inline ll madd(ll a,ll b){return (a%modd+b%modd)%modd;}//加后取模 
inline ll mmul(ll a,ll b){return a%modd * b%modd;}//乘后取模 
int n,m,t;
char s[maxn]; 
struct SAM
{
    int next[maxn][30],fa[maxn],len[maxn];
    ll dp[maxn];
    int root,tot,last;
    int newnode(int l)//创建一个新的状态节点 
    {
        fa[tot]=-1;
        for(int i=0;i<30;++i)  next[tot][i]=-1;
        len[tot++]=l; return tot-1;
    }
    void init()//后缀自动机的初始化 
   {
        tot=0;
        last=root=newnode(0);
        memset(dp,-1,sizeof(dp));
    }
    void extend(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 x)
    {
    	if(dp[x]!=-1) return dp[x];
    	ll sum=1;
    	for(int i=0;i<26;i++)
    	{
    		int z=next[x][i];if(z==-1) continue;
    		sum+=dfs(z);
		}
		return dp[x]=sum;//记忆化搜索 
	} 
}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 extend(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);
	int n=strlen(s);
	sam.init();pam.init();
	for(int i=0;i<n;i++) sam.extend(s[i]-'a');
	sam.extend(26);
	for(int i=n-1;i>=0;i--) sam.extend(s[i]-'a');
	for(int i=0;i<n;i++) pam.extend(s[i]-'a');
	ll fin=(sam.dfs(sam.root)+pam.tot-3)/2;
	printf("%lld\n",fin);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值