HDU 5785 Interesting(回文树)

[题目链接]

[题意]
给定字符串s,找所有的三元组i,j,k满足s[i,j]以及s[j+1,k]均为回文串,求所有满足条件的三元组的i*k的和。

[分析]

回想起以前刷回文树专题时做过的一道题HDU 5157,求不重叠回文子串的对数,做法类似

回文树中,num[now]表示添加该字符后新产生的回文串个数,即以该字符为结尾的回文串个数,num[now] = num[fail[now]]+1
正着跑一次,同时求出num的前缀和,再反着跑一次,同时累加prefixSum[i-1]*suffixSum[i]即可

这道题中则要求以该字符为结尾的回文串的起始坐标和,不方便直接维护和递推
后来想到,sum[now]表示以该字符为结尾的回文串长度和,则sum[now] = sum[fail[now]] + len[now]
则 (pos[now]+1)*num[now] - sum[now] 即是起始坐标和,反着跑时也可以类似的求出终止坐标和,相乘累加即可

这题有点卡空间,把LL改为int,删掉没用的cnt数组后,刚好卡过
感觉manacher做法有点恶心就没敢写,以后抽空补掉吧。

[代码]

#include <bits/stdc++.h>
using namespace std ;
const int N = 1e6 + 5 ;
const int C = 26 ;
const int mod = 1000000007 ;
typedef long long LL ;

int go[N][C] , fail[N] , len[N] , S[N] ;
int num[N] , sum[N] , suf[N] ;
int last , n , tot ;

int newNode( int x )
{
    memset(go[tot],0,sizeof(go[tot])) ;
    num[tot] = sum[tot] = 0 ;
    len[tot] = x ;
    return tot++ ;
}

void init()
{
    tot = n = last = 0 ;
    newNode(0) ;
    newNode(-1) ;
    S[n] = -1 ;
    fail[0] = 1 ;
}

int getFail( int x )
{
    while( S[n-len[x]-1] != S[n] ) x = fail[x] ;
    return x ;
}

void insert( int x , int v )
{
    S[++n] = x ;
    int p = getFail(last) ;
    if( !go[p][x] )
    {
        int now = newNode(len[p]+2) ;
        fail[now] = go[getFail(fail[p])][x] ;
        num[now] = num[fail[now]] + 1 ;
        sum[now] = (sum[fail[now]] + len[now]) % mod ;
        go[p][x] = now ;
    }
    last = go[p][x] ;
}

void build( char *s )
{
    init() ;
    for( LL i = 0 ; s[i] ; i++ )
    {
        insert(s[i]-'a',i+1) ;
        suf[i+1] = (num[last]*(i+2)%mod - sum[last] + mod) % mod ;
    }
}

char s[N] ;

int main()
{
    while( ~scanf( "%s" , s ) )
    {
        int len = strlen(s) ;
        build(s) ;
        LL ans = 0 ;
        init() ;
        for( LL i = len-1 ; i >= 0 ; i-- )
        {
            insert(s[i]-'a',i+1) ;
            ans += suf[i]*(num[last]*i%mod + sum[last]) ;
            ans %= mod ;
        }
        printf( "%I64d\n" , ans ) ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值