题目链接:https://nanti.jisuanke.com/t/41389
题意: 求字符串s中的所有回文子串的值之和(每个回文串不同字母个数表示它的值);
样例输入复制
abac
样例输出复制
6
解释 abac中有 a, b, a, c, aba 这5个回文子串 其中值得和为 1+1+1+1+2 = 6;
思路: 利用回文树也就是回文自动机 求每个回文字符串的用二进制记录某字符的出现是否存在 最终累加所有回文字符串的出现次数乘它不同字母数相加即可。。。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int mx = 3e5+5;
struct pam {
int tot, last;
long long mX, coun[mx];
char s[mx];
struct node {
int len, fail, si, nex[26];
void newtr(int l, int fl, int sk) {
memset(nex, 0, sizeof(nex));
len = l;
fail = fl;
si = sk;
}
}N[mx];///树里存的是字符串的类型
void init() {
N[0].newtr(0, 1, 0);
N[1].newtr(-1, 1, 0);
///两个树根和长度0偶1奇,-1为了表示加2-1为奇数
memset(coun, 0, sizeof(coun));
///记录结尾为某节点的数量
tot = 1;
}
int getfil(int New,int id) {
while (s[id-N[New].len-1] != s[id]) {
New = N[New].fail;
///找末尾位为s[id-1]的回文后缀从大找到小
}
return New;
///最终找到的是末尾为s[id]的最大回文字符串
}
void insert(int id) {
last = getfil(last, id);
int c = s[id]-'a';
if (!N[last].nex[c]) {
int k = getfil(N[last].fail, id);
N[++tot].newtr(N[last].len+2, N[k].nex[c], N[last].si|(1<<c));
///长度为+2回文两边都有,找到第二长的以id为结尾的回文串
N[last].nex[c] = tot;
}
last = N[last].nex[c];
coun[last]++;
}
int qry(int x) {
int sum = 0;
while (x) {
if(x%2) sum++;
x/=2;
}
return sum;
}
void ans() {
for (int i = tot; i > 1; --i) {
coun[N[i].fail] += coun[i];
///计算最终的以i节点为结尾的回文串个数
mX += coun[i]*qry(N[i].si);
}
}
}P;
int main() {
while (~scanf("%s", P.s)){
P.mX = 0;
P.init();
for (int i = 0; P.s[i]; ++i) P.insert(i);
P.ans();
printf("%lld\n", P.mX);
}
}