考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。
题解:
首先,我们可以有个最基本的思路,可以用manacher求出所有回文串,然后hash每个回文串来统计答案,但是这样显然O()的,我们需要考虑优化这个算法。我们发现统计一个回文串时,我们要把这个回文串全都扫一遍,这太慢了,那有什么办法可以简化呢?trie树即可,我们每次把回文串加到trie树里面,每次我们在再末节点+1,那么一个串的出现次数就是它的子树的和(其实就是差分);至于怎么建图,我们把每个节点表示的串hash一遍,然后manacher的时候就可以快速的找到这个点,匹配的时候一个一个加点就好了;因为只有在最有节点向右移动的时候才会加点,所以trie图里的点数不会超过n;具体可以看我的代码work那一段,trie树太丑了,最好别看
再贴上自己的代码,话说手测过了,交bzoj怎么交怎么Wa,刚开始数组还开大了,MLE好多次……
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const int maxn = 700000;
const ll seed = 30077;
char s[maxn];
void init(){
scanf("%s", s + 1);
}
int get(char c){
if (c >= 'a' && c <= 'z') return c - 'a' + 1;
if (c == '#') return 27; if (c == '!') return 28;
return 29;
}
ll pw[maxn], hash[maxn];
struct Ttrie{
struct Thash{
static const int mod = 233333;
int now[mod], pre[maxn];ll son[maxn]; int v[maxn];
int tot;
int find(ll x){
ll tmp = x % mod;
for (int p = now[tmp]; p; p = pre[p])
if (son[p] == x) return v[p];
return -1;
}
void ins(ll x, int pos){
ll tmp = x % mod;
pre[++ tot] = now[tmp];
now[tmp] = tot; son[tot] = x;
v[tot] = pos;
}
void clear(){ tot = 0; memset(now, 0, sizeof(now)); }
}hash;
int son[maxn][30], tot, v[maxn];
void clear(){
memset(son, 0, sizeof(son));
memset(v, 0, sizeof(v));
tot = 0; hash.clear();
}
int get(ll tmp){ return hash.find(tmp); }
int Ins(int now, int x, ll f){
int tmp = hash.find(f);
if (tmp != -1) return tmp;
++ tot; hash.ins(f, tot);
son[now][x] = tot;
return tot;
}
void add(int now){ v[now] ++; }
int h[maxn][2], dep[maxn];
ll getans(){
memset(h, 0, sizeof(h));
memset(dep, 0, sizeof(dep));
int tt = 0, ww = 1; h[0][0] = 0;
while (tt != ww){
tt ++;
for (int i = 1; i <= 26; i ++)
if (son[h[tt][0]][i]){
h[++ ww][0] = son[h[tt][0]][i];
h[ww][1] = h[tt][0];
dep[h[ww][0]] = dep[h[tt][0]] + 2;
if (h[tt][0] == 0) dep[h[ww][0]] --;
}
if (son[h[tt][0]][27]){
h[++ ww][0] = son[h[tt][0]][27];
h[ww][1] = h[tt][0];
dep[h[ww][0]] = dep[h[tt][0]];
}
}
ll ans = 0;
while (ww){
v[h[ww][1]] += v[h[ww][0]];
ans = max(ans, (ll)dep[h[ww][0]] * v[h[ww][0]]);
ww --;
}
return ans;
}
}T;
int f[maxn];
void work(){
T.clear();
int n = strlen(s + 1);
for (int i = n; i; i --)
s[i * 2] = s[i], s[i * 2 + 1] = '#';
s[0] = '!', s[1] = '#', s[2 * n + 2] = '%';
int id = 0; n = n * 2 + 2; pw[0] = 1;
for (int i = 1; i <= n; i ++){
hash[i] = hash[i - 1] * seed + get(s[i]);
pw[i] = pw[i - 1] * seed;
}
for (int i = 1; i < n; i ++){
ll now = 0, tmp = 0;
if (f[id] + id > i){
f[i] = min(f[id * 2 - i], f[id] + id - i);
tmp = hash[f[i] + i - 1] - hash[i - 1] * pw[f[i]];
now = T.get(tmp);
}else f[i] = 1, tmp = get(s[i]), now = T.Ins(0, get(s[i]), tmp);
while (s[i + f[i]] == s[i - f[i]]){
tmp = tmp * seed + get(s[i - f[i]]);
now = T.Ins(now, get(s[i + f[i]]), tmp);
++ f[i];
}
if (id + f[id] < i + f[i]) id = i;
T.add(now);
}
printf("%lld\n", T.getans());
}
int main(){
init();
work();
return 0;
}