题目描述
鳟鱼是一个精通魔法的大法师,现在他有一串咒语,是一个仅用小写字母构成的字符串s(1<=|s|<=300000)。
这个字符串个每个不同的回文子串(起始位置不同算相同,如"aa"中只有一个"a"回文子串)会造成1点A类伤害,每个位置不同(即起始位置不同也算不同,如"aa"中有两个位置不同的"a"回文子串)的回文子串会造成1点B类伤害。
鳟鱼想要知道他施这个魔法会对敌人造成多少点A类伤害,多少点B类伤害。
鳟鱼现在把这个任务交给了你。
输入格式
一个字符串s。
输出格式
两个整数a、b,代表a点A类伤害,b点B类伤害。
样例
输入样例1
xibobix
输出样例1
7 10
数据范围与提示
1<=|s|<=300000。
字符串只包含小写字母
我们先考虑这道题的第一部分。先把回文树建出来,很容易可以想到不同的回文子串的个数即为回文树节点的个数-2(除去odd和even)。
我们再考虑第二部分。我们用前缀和的思想,因为我们的回文树是增量构造的,假设我们已经求出s的数量,则sc的数量的s的数量+以c为结尾的回文串的数量,也就是最长回文后缀的fail链的长度-2(除去odd和even)。这样我们就求出了两种情况的答案。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 400000; char s[N]; int lens; long long ans; namespace Plalindromic_Tree{ struct node{ int go[26]; int fail, len; long long fail_len; }pt[N]; int lst = 0, tot = 0; int cnt[N]; void build() { s[0] = -1; pt[++tot].len = -1; pt[0].fail = pt[1].fail = 1; pt[1].fail_len = 1; pt[0].fail_len = 2; } int add(int c, int n) { int p = lst; while (s[n - pt[p].len - 1] != s[n]) p = pt[p].fail; if (!pt[p].go[c]) { int v = ++tot, k = pt[p].fail; pt[v].len = pt[p].len + 2; while (s[n - pt[k].len - 1] != s[n]) k = pt[k].fail; pt[v].fail = pt[k].go[c]; pt[v].fail_len = pt[pt[k].go[c]].fail_len + 1; pt[p].go[c] = v; } cnt[pt[p].go[c]]++; return lst = pt[p].go[c]; } }using namespace Plalindromic_Tree; int main() { scanf("%s", s + 1); lens = strlen(s + 1); build(); for (int i = 1; i <= lens; i++) { ans += pt[add(s[i] - 'a', i)].fail_len - 2; } cout << tot - 1 << " " << ans; return 0; }