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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章