HDU 5157(回文树)

传送门

题面:
 

Harry got a string T, he wanted to know the number of T’s disjoint palindrome substring pairs. A string is considered to be palindrome if and only if it reads the same backward or forward. For two substrings of T:x=T[a1…b1],y=T[a2…b2]T:x=T[a1…b1],y=T[a2…b2](where a1 is the beginning index of x,b1x,b1 is the ending index of x. a2,b2a2,b2 as the same of y), if both x and y are palindromes and b1<a2 or b2<a1b1<a2 or b2<a1 then we consider (x, y) to be a disjoint palindrome substring pair of T.

Input

There are several cases. 
For each test case, there is a string T in the first line, which is composed by lowercase characters. The length of T is in the range of [1,100000].

Output

For each test case, output one number in a line, indecates the answer.

Sample Input

aca
aaaa

Sample Output

3
15

Hint

For the first test case there are 4 palindrome substrings of T.
They are:
S1=T[0,0]
S2=T[0,2]
S3=T[1,1]
S4=T[2,2]
And there are 3 disjoint palindrome substring pairs.
They are:
(S1,S3) (S1,S4) (S3,S4).
So the answer is 3.

题意:

    给你一个字符串,让你求不相交的回文子串的对数。

题目分析:

    回文树num数组的运用。(又学到了骚操作了呢)

    因为我们要处理的是不相交的回文串,因此对于一个长度为len的字符串,倘若在第i个位置,我们知道了字符串[0,i]中回文子串的个数size1,又知道字符串[i+1,len-1]中回文子串的个数size2,那么当前位置对答案的贡献则是size1*size2。

    而此时我们需要求的每一个位置的回文子串的个数,我们可以通过回文树中的num数组(即表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数)去解决。

    我们只需要先正向建立回文树,先统计出对于每一个字符的num[i],并统计前缀和,最后再反向建树,最后直接将每一部分的相乘并统计答案即可。

代码:

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
struct PAM{//回文树
    int next[maxn][26],fail[maxn],cnt[maxn],len[maxn],num[maxn],S[maxn];
    int id,last,n;
    int newnode(int x){
        for(int i=0;i<26;i++){
            next[id][i]=0;
        }
        cnt[id]=0;
        len[id]=x;
        num[id]=0;
        return id++;
    }
    void init(){
        id=0;
        newnode(0);
        newnode(-1);
        fail[0]=1;
        last=n=0;
        S[n]=-1;
    }
    int getfail(int x){
        while(S[n-len[x]-1]!=S[n]) x=fail[x];
        return x;
    }
    int Insert(int c){
        S[++n]=c;
        int cur=getfail(last);
        if(!next[cur][c]){
            int now=newnode(len[cur]+2);
            fail[now]=next[getfail(fail[cur])][c];
            next[cur][c]=now;
            num[now]=num[fail[now]]+1;
        }
        last=next[cur][c];
        return num[last];
    }
}pam;
ll sum[maxn];
char str[maxn];
int main()
{
    while(~scanf("%s",str+1)){
        int len=strlen(str+1);
        pam.init();
        sum[0]=0;
        for(int i=1;i<=len;i++){//正向建树并统计前缀和
            sum[i]=sum[i-1]+pam.Insert(str[i]-'a');
        }
        ll res=0;
        pam.init();
        for(int i=len;i>=1;i--){//反向建树并统计答案
            res+=sum[i-1]*pam.Insert(str[i]-'a');
        }
        printf("%lld\n",res);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值