题目大意:有一个长度为n的字符串,问分别以k,f,c结尾的回文串有多少个
思路:先用manacher算法(Manacher算法总结_dyx心心的博客-优快云博客_manacher算法)得到以每个字符串为中心,最大的回文半径是多少,同时对于遍历到的每个回文串,如果结尾是k,f,c,就计数,最后累加统计的数量
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
char a[maxn], s[maxn << 1];//a为原字符串,S为添加分隔符后的字符串
typedef long long ll;
int n,hw[maxn<<1],hwk[maxn<<1],hwf[maxn<<1],hwc[maxn<<1], ans;//hw[i]表示以i为中心的最大回文半径
inline void change() {
s[0] = '~', s[1] = '#';
for (int i = 0; i < n; i++) {
s[i * 2 + 2] = a[i];
s[i * 2 + 3] = '#';
}
n = n * 2 + 2;
s[n] = '@';//在每个字符中间插入一个无关字符,从而避免讨论长度的奇偶问题
}
inline void manacher() {
int maxright = 0, mid;//记录当前最长回文串的右端点和中心
for (int i = 1; i < n; i++) {
if (i < maxright)//当前的点在最长回文串内
{
int opi = (mid << 1) - i;//当前点关于最长回文串中点的对称点(mid-(i-mid))
hw[i] = min(hw[opi], hw[mid] + mid - i);//以当前为中心的回文串长度等于关于中点对称点的长度和到右端点的长度的最小值(证明见思路中的链接)
if(hw[i]==hw[opi])
{//如果当前点的回文串和对称点的回文串是对称的,那么回文串长度相等
hwk[i] = hwk[opi];
hwf[i] = hwf[opi];
hwc[i] = hwc[opi];
}
else
hw[i]=0;//不对称的话长度就是0
}
else
hw[i] = 0;//当前点不在最长回文串内了,需要重新查找,初始化长度为0
while (s[i + hw[i]] == s[i - hw[i]])
{//不断扩展回文串
if (s[i + hw[i]] == 'k')
hwk[i]++;
else if (s[i + hw[i]] == 'f')
hwf[i]++;
else if (s[i + hw[i]] == 'c')
hwc[i]++;
++hw[i];//计数
}
if (hw[i] + i > maxright) {//如果当前点的回文串的右端点在最长回文串之外,重新开始计算
maxright = hw[i] + i - 1;
mid = i;
}
}
}
int main() {
scanf("%d", &n);
cin >> a;
change();
manacher();
ll ans1 = 0, ans2 = 0, ans3 = 0;//理论上有可能爆int
for (int i = 0; i < 2*n+2; i++)
{
ans1 += hwk[i];
ans2 += hwf[i];
ans3 += hwc[i];
}
cout << ans1 << " " << ans2 << " " << ans3 << endl;
return 0;
}