[ybt gold medal navigation 2-1-3] [Luogu p4555] longest double palindrome string (two methods)

Longest double palindrome string

Title Link: ybt gold navigation 2-1-3 / luogu P4555

General idea of the topic

Give you a string and ask you to find the longest substring so that it is composed of two palindrome strings.

thinking

First, let's see the palindrome string. Let's run a Manacher first.

Then we'll think about what to do.
First, let's talk about the method of segment tree, and then talk about the better DP method.

Segment tree

Then you will think that you need to find two points as the center of two palindrome strings, and then their longest palindrome string can be connected or intersected.
Because in this way, you can spell the string required by a topic, and its length is the direct distance between the two points multiplied by two.

Then you can enumerate the points, and then how many right endpoints of the points in front of it are behind your left endpoint. Find the point with the smallest number among the points that meet the requirements.
Then you naturally think of using segment trees.

That's OK, but because you deal with the string at the beginning, the final answer has to be divided by two, which offsets the multiplication of two calculated previously, and is directly the length between the two points.

DP

The above method is nlogn. Can you O(n)?

In fact, you can start.
Since it is divided into two palindromes, let's consider where the segmentation point is. Then extend the left and right sides as far as possible.

Then we are actually asking for two arrays.
l l i ll_i lli , denotes i i i is the longest length of the palindrome string at the left end of the palindrome string, r r i rr_i rri , denotes i i i is the longest length of the palindrome string at the right end of the palindrome string.

Then consider what you want.
It cannot be calculated only by the length obtained by Manacher, because it is only the longest centered on it, and the second longest or others may become the best in other positions.
And you can't enumerate the length one by one, which will timeout.

Then you can think about it.
with l l i ll_i For example, you can enumerate from right to left # \# #(Manacher separator), and then l l i = max ⁡ { l l i , l l i + 2 − 2 } ll_i=\max\{ll_i,ll_{i+2}-2\} lli​=max{lli​,lli+2​−2}

What does that mean?
In fact, it means that it can make you come farther, and then reduce a length.

This is the picture:

then r r i rr_i rri # did the same and got these two.

Then we enumerate i i As endpoint i, l l i + r r i ll_i+rr_i The maximum value of lli + rri +.

But it must be composed of two palindrome strings, so you must ensure that l l i > 0 ll_i>0 lli > 0 and r r i > 0 rr_i>0 rri > 0 can be used to compare the maximum value.

Code (segment tree + Manacher)

#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 2147483647

using namespace std;

char s[300001], c[300001];
int n, dis[300001], nn, ans;
int fr[300001], tr[300001];
int tree[1600001], maxdis;

void up(int now) {
	tree[now] = min(tree[now << 1], tree[now << 1 | 1]);
}

void build(int now, int l, int r) {//initialization
	tree[now] = INF;
	
	if (l == r) {
		return ;
	}
	
	int mid = (l + r) >> 1;
	build(now << 1, l, mid);
	build(now << 1 | 1, mid + 1, r);
	
	up(now);
}

int get_min(int now, int l, int r, int L, int R) {//Find the minimum right boundary satisfied
	if (l > r) return 0;
	if (l >= L && r <= R) {
		return tree[now];
	}
	
	int mid = (l + r) >> 1, re = INF;
	if (mid >= L) re = min(re, get_min(now << 1, l, mid, L, R));
	if (mid < R) re = min(re, get_min(now << 1 | 1, mid + 1, r, L, R));
	return re;
}

void insert(int now, int l, int r, int pl, int num) {//Insert right border
	if (l == r) {
		tree[now] = min(tree[now], num);
		return ;
	}
	
	int mid = (l + r) >> 1;
	if (pl <= mid) insert(now << 1, l, mid, pl, num);
		else insert(now << 1 | 1, mid + 1, r, pl, num);
	
	up(now);
}

void manacher() {//Run Manacher, the same below
	int r = -1, mid = -1;
	for (int i = 1; i <= nn; i++) {
		if (i > r) dis[i] = 1;
			else dis[i] = min(dis[2 * mid - i], r - i);
		while (i - dis[i] >= 1 && i + dis[i] <= nn && c[i + dis[i]] == c[i - dis[i]])
			dis[i]++;
		if (i + dis[i] > r) {
			r = i + dis[i] - 1;
			mid = i;
		}
		
		ans = max(ans, i - get_min(1, 1, nn, i - dis[i] + 1, nn));
		insert(1, 1, nn, i + dis[i], i);
		maxdis = max(maxdis, dis[i]);
	}
}
int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	
	nn = 2 * n + 1;
	build(1, 1, nn);
	for (int i = 1; i <= nn; i++)
		if (i & 1) c[i] = '#';
			else c[i] = s[i / 2];
	
	manacher();
	
	if (maxdis - 1 == n && ans == n) printf("%d", n - 1);//It must be divided into two
		else printf("%d", ans);
	
	return 0;
}

Code (DP+Manacher)

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

char s[300001], c[300001];
int n, dis[300001], nn, ans;
int ll[300001], rr[300001];

void manacher() {
	int r = -1, mid = -1;
	for (int i = 1; i <= nn; i++) {
		if (i > r) dis[i] = 1;
			else dis[i] = min(dis[2 * mid - i], r - i);
		while (i - dis[i] >= 1 && i + dis[i] <= nn && c[i + dis[i]] == c[i - dis[i]])
			dis[i]++;
		if (i + dis[i] > r) {
			r = i + dis[i];
			mid = i;
		}
		
		ll[i + dis[i] - 1] = max(ll[i + dis[i] - 1], dis[i] - 1);//Record the left and right boundaries
		rr[i - dis[i] + 1] = max(rr[i - dis[i] + 1], dis[i] - 1);
	}
}

int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	
	nn = 2 * n + 1;
	for (int i = 1; i <= nn; i++)
		if (i & 1) c[i] = '#';
			else c[i] = s[i / 2];
	
	manacher();
	
	for (int i = 3; i <= nn; i += 2)//DP shows that all palindrome strings are leftmost and rightmost (the first one is the one with the longest center point of each)
		rr[i] = max(rr[i], rr[i - 2] - 2);
	for (int i = nn - 2; i >= 1; i -= 2)
		ll[i] = max(ll[i], ll[i + 2] - 2);
	
	for (int i = 3; i <= nn; i += 2)//Enumerate split points
		if (ll[i] && rr[i]) ans = max(ll[i] + rr[i], ans);
	
	printf("%d", ans);
	
	return 0;
}

Keywords: data structure string dp manacher

Added by Ward on Fri, 18 Feb 2022 14:17:09 +0200