[kuangbin专题] Manacher
A - Palindrome
这道题是一道板子题,就是求最长回文串长度,直接构造mp数组然后遍历一遍数组就可以了,或者构造mp数组时直接记录最大值然后返回也可以。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1000010;
int mp[maxn*2];
char ma[maxn*2];
void Manacher(char s[], int len)
{
int l = 0;
ma[l++] = '$';
ma[l++] = '#';
for(int i=0;i<len;i++){
ma[l++] = s[i];
ma[l++] = '#';
}
ma[l] = 0;
int mx = 0;
int id = 0;
for(int i=0;i<l;i++){
mp[i] = mx>i?min(mp[id*2-i],mx-i):1;
while(ma[i+mp[i]] == ma[i-mp[i]]) mp[i]++;
if(mx<i+mp[i]){
mx = i+mp[i];
id = i;
}
}
}
char s[maxn];
int main()
{
int cnt = 0;
while(1){
scanf("%s",&s);
if(strcmp(s,"END")==0)
break;
cnt++;
int len = strlen(s);
Manacher(s,len);
int ans = 0;
for(int i=0;i<2*len+2;i++){
ans = max(ans,mp[i]-1);
}
printf("Case %d: %d\n",cnt,ans);
}
return 0;
}
B - 吉哥系列故事——完美队形II
这道题有一个小小的变化就是,要求从中间到两边,身高不下降,即h[1]<=h[2]<=h[3]...<=h[mid]。这道题本质还是一个板子题,只是在whlie循环向两边找时加上这个不下降的条件即可,另外这个回文串不是字符串而是一个int型数组,所以不能插入#等字符,按照算法原来的意思是插入一个不可能出现的字符所以插入0或者-1或者小于0的任何一个数都可以。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 100005;
int mp[maxn<<1];
int ma[maxn<<1];
int manacher(int s[],int len)
{
int mm = -1;
int l=0;
ma[l++] = -1;
ma[l++] = 0;
for(int i=0;i<len;i++){
ma[l++] = s[i];
ma[l++] = 0;
}
ma[l] = -2;
int mx= 0;
int id = 0;
for(int i=0;i<l;i++){
mp[i] = mx>i?min(mp[2*id-i],mx-i):1;
while(ma[i+mp[i]]==ma[i-mp[i]]&&ma[mp[i]+i] <= ma[mp[i]+i-2]) mp[i]++;
if(i+mp[i]>mx){
mx = i+mp[i];
id = i;
}
//printf("%d\n",mp[i]);
mm = max(mm, mp[i]-1);
}
return mm;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int s[maxn];
int n;
mem(mp, 0);
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &s[i]);
}
int ans = manacher(s,n);
printf("%d\n", ans);
}
return 0;
}
C - Girls' research
这道题的题意是,输入第一个字符代表a,然后后面是以第一个字符为基准的,同样是求回文串,但是要换为真实的串就是把第一个字符作为a的那个对应的真实的串。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char c,str[200009];
char s[400009];
int p[400009];
int init( ){
int cur= 1;
s[0]='$';
s[1]='#';
int len = strlen( str );
for( int i=0;i<len;i++){
s[++cur]=str[i];
s[++cur]='#';
}
return cur;
}
void manacher( ){
int len = init();
int id ,mx =0;
int max_len = -1,index ;
for( int i=1;i<=len;i++){
if( i < mx ){
p[i] = min( mx - i , p[id*2-i] );
}
else p[i]=1;
while( s[i - p[i]] == s[i + p[i]] )
p[i]++;
if( i + p[i] > mx ){
mx = i + p[i];
id = i;
}
if( p[i] - 1 > max_len ){
index = i ;
max_len = p[i] - 1;
}
}
if( max_len <2 ){
printf("No solution!\n");
}
else {
printf("%d %d\n",(index - max_len +1 )/2-1,(index + max_len -1 )/2-1 );
for( int i= index - max_len +1 ;i<= index + max_len -1;i++ ){
if( s[i]!='$'&&s[i] != '#')
printf("%c",s[i]);
}
printf("\n");
}
}
int main(void){
while( scanf("%c",&c) !=EOF ){
scanf("%s",str);
getchar();
int t = c - 'a';
int len = strlen( str );
for( int i=0;i<len;i++){
str[i] - c >= 0 ? str[i] = 'a' + str[i]-c : str[i] = 'a' + str[i] -c + 26;
}
manacher( );
}
return 0;
}
D - Making Huge Palindromes
这道题题意是,要把所给的字符串补成一个最短的回文串然后输出这个回文串的长度。这道题可以转化成,求出最长且包含最后一个字符的回文串长度,然后用所给串的长度减去这个回文串的长度就是要添加的长度,加上原串长度就是最终结果。
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e6+5;
int mp[maxn<<1];
char ma[maxn<<1];
int l = 0;
void manacher(char s[], int len)
{
ma[l++] = '$';
ma[l++] = '#';
for(int i=0;i<len;i++){
ma[l++] = s[i];
ma[l++] = '#';
}
int mx = 0;
int id = 0;
ma[l] = 0;
for(int i=0;i<l;i++){
mp[i] = mx>i?min(mp[id*2-i],mx-i):1;
while(ma[i+mp[i]] == ma[i-mp[i]]) mp[i]++;
if(mx<i+mp[i]){
mx = i+mp[i];
id = i;
}
}
return ;
}
int main()
{
int t=0;
scanf("%d",&t);
for(int k=1;k<=t;k++){
memset(mp,0,sizeof(mp));
l = 0;
int ans = -1;
char s[maxn];
scanf("%s",&s);
getchar();
manacher(s,strlen(s));
for(int i=0;i<l;i++){
if(i+mp[i] == l){
ans = max(ans,mp[i]-1);
}
}
printf("Case %d: %d\n",k,2*strlen(s)-ans);
}
return 0;
}
这就是manacher算法的几个基础题了,进阶题后续会给出。