[The Preliminary Contest for ICPC Asia Xuzhou 2019 - 徐州网络赛G] Colorful String

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。

题意

给定一个字符串,要求找出所有回文子串中不同字母的个数的总和。

思路

首先马拉车,得到每一个回文子串的长度。每个回文串中都可能会有重复的字母,所以不能对回文串的长度进行简单加减。要知道每个回文串中,有多少个不同的字母,暴力肯定是不行的,因为给个全相同的序列就会变成O(n^2)。

为了快速得知,回文串中有多少个不同字母,我们可以先对字符串进行一个预处理。由于回文串的性质,我们只需要计算回文串右半部分(奇数长时含中心)的不同字母数量即可,因此,我们可以对于每个位置 i,找出并记录 26 个字母在其右边第一次出现的位置 j,这个过程可以由 O(26*n) 的复杂度完成。得到这个初次出现表后,我们即可对马拉车得到的各回文串进行遍历,当回文串的长度大于等于 1 时(p[i] - 1 >= 1)我们才处理这个回文串,计算得到该回文串的中心位置 mid 以及该回文串的右边界 r(r不包含在回文串内),然后遍历初次出现表的 26 个字母,若某个字母 c 的初次出现位置 v < mid + r,说明该字母在当前的回文串内,且能够被当前的回文串的子串重复计算累计 r - v 次,将答案加上 r - v 即可。遍历完所有回文子串后,即可得到答案。

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 3e5+5;
string t, s;
int p[maxn<<1], v[maxn][30];

void read()
{
    cin >> t;
}

void init() //预处理位置i后字母j第一次出现的位置,如没有,则为-1
{
    int len = t.length();
    for(int i = 0; i < 26; ++i)
        v[len][i] = -1;
    int c;
    for(int i = len-1; i >= 0; --i)
    {
        c = t[i] - 'a';
        v[i][c] = i;
        for(int j = 0; j < 26; ++j)
            if(j != c)
                v[i][j] = v[i+1][j];
    }
}

void manacher()
{
    s = "$#";
    for(int i = 0; i < t.length(); ++i)
    {
        s += t[i];
        s += '#';
    }

    int mid = 1, r = 1;
    for(int i = 1; i < s.length(); ++i)
    {
        if(i < r)
            p[i] = min(p[mid*2-i], r-i);
        else
            p[i] = 1;
        while(s[i - p[i]] == s[i + p[i]])
            ++p[i];
        if(r < i + p[i])
        {
            mid = i;
            r = i + p[i];
        }
    }
}

void solve()
{
    init();
    manacher();

    ll ans = 0, mid, r;
    for(int i = 1; i < s.length(); ++i)
    {
        if(p[i] < 2) //不构成回文串的分隔符
            continue;
        mid = ((i+1) >> 1) - 1; //得到回文中心
        r = mid + (p[i] >> 1); //得到回文串的右边界(不含)
        for(int j = 0; j < 26; ++j)
            if(~v[mid][j] && v[mid][j] < r) //如果回文中心的右边出现了字母j且在回文串的右边界内
                ans += (r - v[mid][j]); //能够为该字母出现的位置(含)后的回文串贡献答案+1
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    read();
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/HNUCSEE_LJK/article/details/100681087