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

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 
#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);
    }
}

 

你可能感兴趣的:(回文树)