The Preliminary Contest for ICPC Asia Xuzhou 2019 G. Colorful String(回文树)

The value of a string ss is equal to the number of different letters which appear in this string.

Your task is to calculate the total value of all the palindrome substring.

Input

The input consists of a single string |s|(1 \le |s| \le 3 \times 10^5)∣s∣(1≤∣s∣≤3×105).

The string ss only contains lowercase letters.

Output

Output an integer that denotes the answer.

样例输入复制

abac

样例输出复制

6

样例解释

abacabac has palindrome substrings a,b,a,c,abaa,b,a,c,aba,ans the total value is equal to 1+1+1+1+2=61+1+1+1+2=6。

 

题意:

给了一个字符串,问这个字符串中的回文串中的不同的字母一共有多少个。

思路:

很裸的回文树,因为只有26个字母,用开个大于26位的数记录一下出现的字母就行了。

直接套用去年南京网络赛的板子改一下就行了。

代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn=3e5+5;
typedef long long ll;
char str[maxn];
ll ans;
struct Palindromic_Tree
{
    int next[maxn][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    ll wei[maxn];
    int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[maxn] ; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    ll num[maxn] ; //表示存放的回文串的值
    int len[maxn] ;//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
    int S[maxn] ;//存放添加的字符
    int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。
    int n ;//表示添加的字符个数。
    int p ;//表示添加的节点个数。
    int get(ll x)
    {
        int ans=0;
        for(int i=0;i<26;i++){
            if(x>>i&1) ans++;
        }
        return ans;
    }
    int newnode ( int l )  //新建节点
    {
        for ( int i = 0 ; i < 26 ; i++ )
            next[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }

    void init ()  //初始化
    {
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }

    int get_fail ( int x )  //和KMP一样,失配后找一个尽量最长的
    {
        while ( S[n - len[x] - 1] != S[n] )
            x = fail[x] ;
        return x ;
    }

    void add ( int c )
    {
        S[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] )  //如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
        {
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now ;
            wei[now]=wei[cur]|(1<<c);
        }
        last = next[cur][c] ;
        cnt[last] ++ ;
    }

    void count ()
    {
        for ( int i = p - 1 ; i >= 0 ; -- i )
            cnt[fail[i]] += cnt[i] ;
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
        for(int i=2;i<=p-1;i++){
            ans=ans+1ll*cnt[i]*get(wei[i]);
        }
    }
} tree;
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    tree.init();
    for(int i=0;i<len;i++)
    {
        tree.add(str[i]-'a');
    }
    tree.count();
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值