【kuangbin专题】Manacher

[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算法的几个基础题了,进阶题后续会给出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值