【字符串 manacher】洛谷_4555 [国家集训队]最长双回文串

题意

输入长度为 n n n的串 S S S,求 S S S的最长双回文子串 T T T,即可将 T T T分为两部分 X , Y , ( ∣ X ∣ , ∣ Y ∣ ≥ 1 ∣ X ∣ , ∣ Y ∣ ≥ 1 ) X,Y,(|X|,|Y|≥1∣X∣,∣Y∣≥1) XYX,Y1X,Y1 X X X Y Y Y都是回文串。

思路

我们可以设 l i l_i li为以 i i i结尾的回文字符串的最长长度, r i r_i ri为以 i i i开头的回文字符串的最长长度。

求这两个东西我们可以用到 m a n a c h e r manacher manacher算法,算法中每次扩展回文串长度时可以顺便更新一下这两个的值,最后递推把所有空隙位置的两个值更新一下最大值,就可以得出这两个东西了。

有了这两个我们就可以很容易得出答案 a n s = m a x ( l i + r i ) { l i > 0 , r i > 0 } ans=max(l_i+r_i)\{l_i>0,r_i>0\} ans=max(li+ri){li>0,ri>0}

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

int n, ans;
int hw[22000002], l[22000002], r[22000002];
char a[11000000], s[22000002];

void init() {
    scanf("%s", a);
    s[0] = s[1] = '#';
    n = strlen(a);
    for (register int i = 0; i < n; i++) {
        s[i * 2 + 2] = a[i];
        s[i * 2 + 3] = '#';
    }
    n = n * 2 + 2;
    s[n] = 0;
}

void manacher() {
    int maxright = 0, mid;
    for (register int i = 1; i < n; i++) {
        if (i < maxright)
            hw[i] = std::min(hw[mid * 2 - i], maxright - i);
        else hw[i] = 1;
        for (; s[i + hw[i]] == s[i - hw[i]]; hw[i]++);
        if (hw[i] + i > maxright) {
            maxright = hw[i] + i;
            mid = i;
        }
        l[i + hw[i] - 1] = std::max(l[i + hw[i] - 1], hw[i] - 1);//更新l,r
        r[i - hw[i] + 1] = std::max(r[i - hw[i] + 1], hw[i] - 1);
    }
}

int main() {
    init();
    manacher();
    for (register int i = 2; i < n; i += 2)//递推
    	r[i] = std::max(r[i], r[i - 2] - 2);//每往里缩,回文长度就会-2
    for (register int i = n - 1; i >= 2; i -= 2)
    	l[i] = std::max(l[i], l[i + 2] - 2);//同理
	for (register int i = 1; i < n; i += 2)
		if (l[i] && r[i])//保证两边都有
			ans = std::max(ans, l[i] + r[i]);
	printf("%d", ans); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值