G. Colorful String
题意: 给出一个字符串,询问不同回文子串的权值之和。每个回文子串的权值为回文子串不同字母的个数。
题解: 回文树预处理出所有本质不同的回文子串及其出现次数,对于区间不同字母个数,可以对26个字母做前缀和。预处理后,计算方式就是每一个本质不同的回文子串的区间不同字母数量乘以出现次数。
实际上用
也可以做这道题的。首先,通过
我们可以先预处理出对于每个位置的最长回文半径,其次我们可以预处理出对于每个位置最近的26个字母。然后我们枚举每个位置,对于在其最长回文半径内的字母,它的贡献是所有长度在
的回文子串数量。最后我们累加每个位置最长回文半径内的字母贡献即可。
代码
- 回文树版本
const int N = 3E5+10;
struct PAM{
int nxt[N][26], fail[N], len[N], cnt[N], S[N], pos[N];
int tot,n,last;
inline int newnode(int x) {
memset(nxt[tot],0,sizeof nxt[tot]);
cnt[tot] = 0, len[tot] = x; return tot++;
}
inline void init() {
newnode(tot = 0), newnode(S[0] = -1), fail[last = n = 0] = 1;
}
inline int getfail(int x) {
while(S[n - len[x] - 1] != S[n]) x = fail[x]; return x;
}
inline void Insert(int c) {
c = c - 'a';
S[++n] = c;
int cur = getfail(last);
if(!nxt[cur][c]) {
int now = newnode(len[cur] + 2);
fail[now] = nxt[getfail(fail[cur])][c];
nxt[cur][c] = now;
}
last = nxt[cur][c]; cnt[last]++;
pos[last] = n;
}
inline void getsum() {
for(int i = tot - 1; i >= 0; --i) {
cnt[fail[i]] += cnt[i];
}
}
}pam;
char s[N];
int sum[N][26];
int getsum(int l,int r)
{
int ret = 0;
for(int i = 0; i < 26; ++i) if(sum[r][i] - sum[l - 1][i] > 0) ret++;
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
ios::sync_with_stdio(false); cin.tie(0);
cin >> s + 1;
int n = strlen(s + 1);
for(int i = 1; i <= n; ++i) {
for(int j = 0; j < 26; ++j) {
sum[i][j] = sum[i - 1][j];
}
sum[i][s[i] - 'a']++;
}
pam.init();
for(int i = 1; i <= n; ++i) {
pam.Insert(s[i]);
}
pam.getsum();
int l,r;
long long ans = 0;
for(int i = 2; i < pam.tot; ++i) {
l = pam.pos[i] - pam.len[i] + 1;
r = pam.pos[i];
ans += 1LL * getsum(l,r) * pam.cnt[i];
}
cout << ans << '\n';
return 0;
}
- 版本
#include<bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#define dbg(args...) \
do{ \
cout << "\033[32;1m" << #args << " -> "; \
err(args); \
} while(0)
#else
#define dbg(...)
#endif
void err()
{
cout << "\033[39;0m" << endl;
}
template <template <typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args)
{
for (auto x : a) cout << x << ' ';
err(args...);
}
template <typename T, typename... Args>
void err(T a, Args... args)
{
cout << a << ' ';
err(args...);
}
/****************************************************************************************************/
typedef long long LL;
const int N = 6E5+10;
int last[N][30],p[N];
char s[N],st[N];
int change()
{
int len=strlen(st);
int j=2;
s[0]='^';
s[1]='$';
for (int i=0;i<len;i++)
{
s[j++]=st[i];
s[j++]='$';
}
s[j]='&';
return j;
}
void init(int len)
{
for(int i = 0; i < 26; ++i) {
last[len][i] = len;
}
for(int i = len - 1; i >= 0; --i) {
for(int j = 0; j < 26; ++j) {
last[i][j] = last[i + 1][j];
}
last[i][st[i] - 'a'] = i;
}
}
LL Manacher()
{
int len = change(), mid = 1, mx = 1;
init(strlen(st));
LL ans = 0;
for (int i = 1; i < len; i++)
{
if (i < mx)
p[i] = min(mx - i, p[mid * 2 - i]);
else
p[i] = 1;
while (s[i - p[i]] == s[i + p[i]])
p[i]++;
if(mx < i + p[i])
{
mid = i;
mx = i + p[i];
}
}
int ori;
for(int i = 1; i < len; ++i) {
if(i & 1)
ori = (i + 1) / 2 - 1;
else
ori = i / 2 - 1;
int cnt = p[i] / 2;
for(int j = 0; j < 26; ++j) {
if(last[ori][j] - ori < cnt) {
ans += cnt - (last[ori][j] - ori);
}
}
}
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
scanf("%s",st);
printf("%lld\n",Manacher());
return 0;
}