回文树

用途

用于解决一些关于回文的问题,处理回文问题的利器。

定义

有2个根,odd和even。

even的长度是0,odd的长度是-1,什么意思呢?

就是比如说插入一个字符’a’,插入even时会变成‘aa’,而插入odd时会变成’a’。

所以odd代表长度为奇数的回文串,而even代表长度为偶数的回文串。

len为一个点所代表的字符串实际长度。

fail为失配指针,指向最长回文后缀。

构建

考虑我们加入一个字符c,假设上一个字符串为 S l . . r S_{l..r} Sl..r,若 S l − 1 S_{l-1} Sl1等于新加入的 c c c,则直接加入即可。

S l − 1 S_{l-1} Sl1不等于 c c c,则我们需要在 S l . . r − 1 S_{l..r-1} Sl..r1找到一个最长的回文后缀,使 S l ′ . . r S_{l'..r} Sl..r为回文串。

所以fail指针指向的就是这个点所代表的字符串的最长回文后缀所在的节点。

如babcbab指向bab。

一些结论

1.对于任意一个串S,它的本质不同的回文串的个数不超过 l e n ( S ) len(S) len(S)

2.在串S后面加入一个字符,新增的本质不同的回文串的个数不超过1个

证明先不写了,数学归纳法。

在插入一个字符串时,我们首先看它新增的回文串是否存在,如存在,则直接跳到相应节点更新信息。

否则新开一个节点来代表这个字符串即可。

新开一个节点后还需要更新这个节点的所有信息即可。

详细细节见代码,例题APIO2014【回文串】

#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long

using namespace std;

const int N = 3e5 + 5;

int n;
char s[N];
ll ans;

struct Palindrome_Automaton
{
	int fail[N],to[N][26],cnt[N],len[N],tot,Len,last;
	void First()
	{
		tot = 1;
		fail[0] = 1,len[1] = -1; // 0代表ven,1代表odd
		last = 0;
		s[0] = -1;
	}
	int Get_Fail(int p) // 依fail链跳转
	{
		while (s[Len - len[p] - 1] != s[Len]) p = fail[p];
		return p;
	}
	void add(int c)
	{
		Len++;
		int cur = Get_Fail(last); // 找到满足条件的地方
		if (!to[cur][c])
		{
			int p = ++tot;
			len[p] = len[cur] + 2;
			fail[p] = to[Get_Fail(fail[cur])][c]; // 注意这里里面不是cur,是fail[cur],因为如果是cur的话就无法更新了。
			to[cur][c] = p;
		}
		last = to[cur][c];
		cnt[last]++;
	}
	void sum()
	{
		for (int i = tot ; i ; i--) 
		{
			cnt[fail[i]] += cnt[i];
			ans = max(ans,1LL * len[i] * cnt[i]);
		}
	}
}P_Tree;

int main()
{
	freopen("palindrome.in","r",stdin);
	freopen("palindrome.out","w",stdout);
	P_Tree.First();
	scanf("%s",s + 1);
	n = strlen(s + 1);
	for (int i = 1 ; i <= n ; i++) P_Tree.add(s[i] - 'a');
	P_Tree.sum();
	printf("%lld\n",ans);
	return 0;
}

复杂度分析

时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的,用到了势能分析,不会

空间复杂度是 O ( n ∗ s t r ( s ) ) O(n*str(s)) O(nstr(s))的, s t r ( s ) str(s) str(s)是字符集大小。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值