[Apio2014]回文串

1 篇文章 0 订阅
1 篇文章 0 订阅


考虑一个只包含小写拉丁字母的字符串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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值