G. Colorful String
題意: 給出一個字符串,詢問不同迴文子串的權值之和。每個迴文子串的權值爲迴文子串不同字母的個數。
題解: 迴文樹預處理出所有本質不同的迴文子串及其出現次數,對於區間不同字母個數,可以對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;
}