TonyYin's Blog

Back

题意#

对于一个字符串 ss,求出一个尽可能短的回文字符串 ss^{*},同时使得 ssss^{*} 的前缀。

s105|s|\leq 10^5.

分析#

前置知识:Manacher - OI Wiki,,题解同步发表于 TonyYin’s Blog

首先,先考虑如何满足 ssss^{*} 的前缀,且 ss^{*} 是回文串这两个条件

经过简单分析可以得到,ss^{*} 回文中心左侧一定都与 ss 的前缀相同。

因此只需要找到最小的 ii,其回文半径可以延续到串末即可。

形式化地,要找到最小的 ii 满足 s[2×ilen]s[i]s[len]s[2\times i-len]\sim s[i]\sim s[len] 为回文串。

实现#

通过 Manacher\rm{Manacher} 算法解决问题。

在进行 Manacher\rm{Manacher} 的过程中,只要当前点 ii 的回文半径可以延续到串末,ii 即为 ss^{*} 的回文中心。

找到中心之后,把 ss^{*} 还原出来即可。

代码#

#include <bits/stdc++.h>
using namespace std;
const int MAXLEN = 1e5 + 10;
char in[MAXLEN], s[MAXLEN << 1];
int len;
void init() {
	int lenin = strlen(in); len = 0;
	s[++len] = '*'; s[++len] = '|';
	for(int i = 0; 
        i < strlen(in); i++) {
		s[++len] = in[i]; s[++len] = '|';
	}
}
int p[MAXLEN << 1];//以i为中心的最大回文半径
int main() {
	while(scanf("%s", in) != EOF) {
		int ans = 0;
		int mx = 0, id = 0; init();
		for(int i = 1; i <= len; i++) {
			if(i < mx) p[i] = min(mx - i, p[2 * id - i]);
			else p[i] = 1;
			while(s[i + p[i]] == s[i - p[i]]) p[i]++;
			if(i + p[i] >= mx) {
				id = i; mx = i + p[i];
			}
			if(mx >= len) {
				ans = i;
				break;
			}
		}
		for(int i = 2; i <= ans; i++) if(s[i] != '|') putchar(s[i]);
		for(int i = ans - 1; i >= 2; i--) if(s[i] != '|') putchar(s[i]);
		putchar('\n');
	}
	return 0;
}
cpp
【UVA-11475】Extend to Palindrome
https://www.tonyyin.top/blog/oi-solution/uva11475
Author TonyYin
Published at August 27, 2021
Comment seems to stuck. Try to refresh?✨