manacher---神奇项链
神奇项链
|
问题描述
母亲节就要到了,小 H 准备送给她一个特殊的项链。
这个项链可以看作一个用小写字母组成的字符串,每个小写字母表示一种颜色。
为了制作这个项链,小 H 购买了两个机器。
第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:
假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。
现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。
输入格式
输入数据有多行,每行一个字符串,表示目标项链的样式。
输出格式
多行,每行一个答案表示最少需要使用第二个机器的次数。
样例输入 1
abcdcba
abacada
abcdef
样例输出 1
0
2
5
样例输入 2
xuqeytcixfzpzvcacymqncdohedfyowmipplplkyrsaspjliczflordhlbckyiuqxkslntofajs
amjmaekzbnbwagotspirvjksendltyeeuswefpdcdmmhzomlvkrhtwidlybkvvvebqkmvednaxddeygghrvqfaxwjssvcphcrzeauwlowwdmhacpzbnihgmbypfsblvsyaugkcg
样例输出 2
65
118
提示
每个测试数据,输入不超过 5行
每行的字符串长度小于等于 50000
1.通过马拉车找出所有回文子串,把每个回文子串看作一条线段
2.问题变成选最少的线段将整个区间覆盖,即最小区间覆盖问题。贪心即可。
#include<stdio.h> #include<bits/stdc++.h> using namespace std; char s[100010]; int len[100010];//回文串长度 int n; struct Node { int l,r; } a[100010]; bool cmp(Node a,Node b)//按左端点贪心排序 { return a.l<b.l; } void Init() { for(int i=n; i>=1; i--) { s[i<<1]=s[i]; s[i<<1|1]='&'; } n=n<<1|1; s[0]='%'; s[1]='&'; s[n+1]='^'; } void manacher() { int maxright=0,num=0; for(int i=1; i<=n; i++) { if(i<maxright) len[i]=min(len[num*2-i],maxright-i); else len[i]=1; while(s[i+len[i]]==s[i-len[i]]) { len[i]++; } if(maxright<i+len[i]) { maxright=i+len[i]; num=i; } } } int main() { while(scanf("%s",s+1)!= EOF)//从第一位开始算 { n=strlen(s+1); Init(); manacher(); for(int i=1; i<=n; i++) { a[i].l=i-len[i]+1;//以i为中心的左长度 a[i].r=i+len[i]-1;//以i为中心的右长度 } sort(a+1,a+1+n,cmp); int r=0,ans=0,i=1; while(i<=n) { int sum=0; while(a[i].l-1<=r&&i<=n) { sum=max(sum,a[i].r); i++; } ans++;//现长 r=sum;//覆盖完全 if(r==n) { break; } //ans++; } cout<<ans-1<<endl; } }