BZOJ 2565: 最长双回文串

本文介绍了一种利用回文树解决最长双回文子串问题的方法,通过正反两次构建回文树,计算出给定字符串中两个相连回文串的最长长度。示例代码展示了如何实现这一算法,包括初始化、添加字符、获取失败指针等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。

Input

一行由小写英文字母组成的字符串S。

Output

一行一个整数,表示最长双回文子串的长度。
Sample Input
baacaabbacabb

Sample Output
12

HINT

样例说明
从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5

思路

这是个裸题,求两个连在一起的回文串的最长长度。

我们可以用回文树来求的
第 i 个字母之前的最长回文串的长度是多少,
然后倒这来就可以求得
第 i 个字母之后的最长回文串的长度是多少,
两个加起来就好了。

这个是回文树的一些主要数组。

Fail 失配指针。像AC自动机差不多的失配指针,这个指向的是同样回文串结尾的最长回文串。
len 当前回文串的长度。
s[] 一个个加入新的字母。
n 当前加入的是第几个字符。
p 当前是第几个节点。
num[i] 代表 i 这个节点所代表的回文串中有多少个本质不同的回文串。
cnt[i] 代表 i 这个节点所代表的回文串一共出现了多少次。 这个最后要 count() 一下。

强有力的博客

https://blog.csdn.net/u013368721/article/details/42100363

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+1000;
int Next[N][30],Fail[N],len[N];
int s[N],last,n,p,num[N],cnt[N],sum[N];
int s1[N],s2[N];


int newnode(int x){  //新加一个节点。
	for (int i = 0; i < 30; i++)
		Next[p][i] = 0;    //加上  i 这个字母可以到达的后继节点。
	cnt[p] = num[p] = 0;
	len[p] = x;
	return p++;
}
void init(){ // 初始化,首先要见两个点,偶数节点,和奇数节点。 
	p = 0;
	newnode(0); newnode(-1);
	last = 0, n = 0;
	s[n] = -1; Fail[0] = 1;
	return;
}
int getfail(int x){
	while(s[n-len[x] - 1] != s[n]) x = Fail[x];  //找到满足的点。 
	return x;
}

int add(int c){
	c -= 'a';
	s[++n] = c;
	int cur = getfail(last);
	if (!Next[cur][c]){ //如果没有后继节点。新加入一个节点。
		int now = newnode(len[cur] + 2);
		Fail[now] = Next[getfail(Fail[cur])][c];
		Next[cur][c] = now;
		num[now] = num[Fail[now]] + 1;  //
	}
	last = Next[cur][c];
	cnt[last]++;
	return len[last];
}
void count(){ // count() 最后计算 cnt[] 
	for (int i = p - 1; i >= 0; --i)
		cnt[Fail[i]] += cnt[i];
}
int main(){
	char t[N];
	int ll;
	scanf("%s",t);
	ll = strlen(t);
	int Max = 0;
	init(); //正着来一遍。
	for (int i = 0; i < ll; i++)
		s1[i] = add(t[i]);
	count();

	init(); //反着来一遍。
	for (int i = ll-1; i >= 0; i--)
		s2[i] = add(t[i]);
	for (int i = 0; i < ll; i++)
		Max = max(Max,s1[i] + s2[i+1]);
	printf("%d\n",Max);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值