Colorful String【迴文串數量】
阿新 • • 發佈:2020-07-01
題意:
給定一個字串,要求找出所有迴文子串中不同字母的個數的總和。
題目連結: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; }