【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\n" , ans - 1); } return 0; }