1. 程式人生 > 實用技巧 >Colorful String【迴文串數量】

Colorful String【迴文串數量】

題意:

給定一個字串,要求找出所有迴文子串中不同字母的個數的總和。
題目連結:https://nanti.jisuanke.com/t/41389

分析:

可以先利用 \(Manacher\) 求出以各個位置為中心的迴文串的長度。關鍵在於如何求出一個迴文串中不同字母的個數,根據迴文串的性質,只需要求出一邊即可。可以利用主席樹,也可以先預處理出,每個位置前的各個字母最後出現的位置。計算時,只要判斷該字母是否在迴文串範圍內,然後根據該位置離迴文串的邊界的距離來確定其貢獻,累加求和。

程式碼:

//迴文串
// 演算法第一步就是:預處理字串,做法是在每一個字元的左右都加上一個特殊字元(前提是這個字元在字串沒有出現過)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+5;
char s[N]; //原始字串
char sn[N << 1]; //擴充後的新字串
int sen[N << 1];//表示以sn[i]為中心的最長迴文的半徑
int pos[N][30];
int init()//對原有的字串進行擴充
{
    int len = strlen(s),cnt=1;//s從0開始存
    sn[0] = '$';
    sn[1] = '#';
    for (int i = 0; i < len;i++)
    {
        sn[++cnt] = s[i];
        sn[++cnt] = '#';
    }
    sn[++cnt] = '\0';
    return cnt;//返回擴充後的字串長度
}
ll manacher()
{
    int len = init();//預處理擴充
    int id, mx = 0;
    for (int i = 1; i < len;i++)
    {
        sen[i] = mx > i ? min(sen[2 * id - i], mx - i) : 1;
        while(sn[i-sen[i]]==sn[i+sen[i]])
            sen[i]++;
        if(mx<i+sen[i])
        {
            id = i;
            mx = i + sen[i];
        }
    }
    ll res=0;
    for(int i=2;i<len-1;i++)
    {
        int p=i/2-1;//cout<<"i="<<i<<endl;
        int t=0;
        if((sen[i]-1)&1) t=sen[i]/2;
        else t=(sen[i]-1)/2;
        for(int j=0;j<26;j++)
        {
            int l=p-t;
            if(pos[p][j]>l) res+=(pos[p][j]-l);
        }
    }
    return res;
}
int main()
{
    scanf("%s", s);
    int len=strlen(s);
    for(int i=0;i<26;i++)
        pos[0][i]=-1;
    pos[0][s[0]-'a']=0;
    for(int i=1;i<len;i++)
    {
        for(int j=0;j<26;j++)
            pos[i][j]=pos[i-1][j];
        pos[i][s[i]-'a']=i;
    }
    printf("%lld\n",manacher());
    return 0;
}