【bzoj3790】神奇项链 Manacher+贪心

原文地址:http://www.cnblogs.com/GXZlegend/p/6803032.html


题目描述

母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 

输入

输入数据有多行,每行一个字符串,表示目标项链的样式。 

输出

多行,每行一个答案表示最少需要使用第二个机器的次数。 

样例输入

abcdcba
abacada
abcdef

样例输出

0
2
5


题解

Manacher+贪心,时间复杂度瓶颈在于排序

首先易知用于覆盖的回文串一定是极长回文串,那么可以先用Manacher求出所有极长回文串。

这样就将问题转化为线段覆盖问题:n条线段用于覆盖,可以重叠,求最少用多少条线段能够覆盖整个[1,n]。

按l排序,贪心就可以了。

依旧是有O(n)的优秀算法

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 250010
using namespace std;
struct data
{
	int l , r;
}a[N];
char str[N] , tmp[N << 1];
int n , p[N << 1];
bool cmp(data a , data b)
{
	return a.l < b.l;
}
int main()
{
	while(scanf("%s" , str + 1) != EOF)
	{
		n = strlen(str + 1);
		int i , mx = 0 , last , tot = 0 , pl = 0 , ans = 0;
		tmp[0] = '0';
		for(i = 1 ; i <= n ; i ++ ) tmp[i * 2 - 1] = '#' , tmp[i * 2] = str[i];
		n = n * 2 + 1 , tmp[n] = '#';
		for(i = 1 ; i <= n ; i ++ )
		{
			if(mx >= i) p[i] = min(p[last * 2 - i] , mx - i + 1);
			else p[i] = 1;
			while(tmp[i - p[i]] == tmp[i + p[i]]) p[i] ++ ;
			if(mx < i + p[i] - 1) mx = i + p[i] - 1 , last = i;
			a[++tot].l = (i - p[i] + 2) / 2 , a[tot].r = (i + p[i] - 2) / 2;
		}
		sort(a + 1 , a + tot + 1 , cmp);
		n /= 2;
		i = 1;
		while(pl < n)
		{
			mx = 0;
			while(a[i].l <= pl + 1) mx = max(mx , a[i].r) , i ++ ;
			ans ++ , pl = mx;
		}
		printf("%d
" , ans - 1);
	}
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/6803032.html