题解:[ABC375D] ABA
ABA
题意:
给定字符串 \(S\),问有多少个三元组 \((i,j,k)\) 满足 \(1 \le i < j < k \le |S|\) 且 \(S_i\),\(S_j\),\(S_k\) 顺次连接构成回文串。
思路:
因为长度是 \(3\),所以只要首尾一样就是回文串。
-
讨论一个位置对答案的贡献
设 \(np\) 表示我们现在讨论的位置,\(p[k][i]\) 表示字母 \(k\) 在第 \(i\) 次出现时所处位置下标,\(c[k][np]\) 表示字母 \(k\) 在 \(np\) 之前出现了几次。
那么一个字母 \(k\) 对答案的贡献就是它前面与它相同的字母与它之间包含的字符个数之和(满足首尾相同,中间字符任选),形式化的表示为:\(\sum\limits_{i=1}^{c[k][np]}(np-p[k][i]-1)\),化简后为:\(c[k][np] \times (np-1) - \sum\limits_{i=1}^{c[k][np]}p[k][i]\)。
-
推广计算
从前向后计算每个位置贡献,可以省略表示位置的一维。另外可以通过前缀和维护,即 \(sum[k]\) 表示字母 \(k\) 截至现在的 \(\sum\limits_{i=1}^{c[k][np]}p[k][i]\) 的值。
注意:
答案超过 int 范围,需要开 long long。
时间复杂度:
计算一个字母贡献为 \(O(1)\),总复杂度为 \(O(|S|)\)。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ans, sum[27], cnt[27];
char c;
int main() {
for(int i=1;;i++) {
c=getchar();
if(c<'A'||c>'Z') break;
ll k=c-'A';
ans+=cnt[k]*(i-1)-sum[k];
sum[k]+=i;
cnt[k]++;
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号