HDOJ3068 最长回文子串-- manacher算法

//主要给自己看,写这个是为了学新的算法加深印象

ACM队这两天新出的字符串专题对我这种渣渣来说是比较难的,什么KMP,manacher,trie字典树,hash,ac自动机,这些我现在就勉强会个kmp算法,这个还是之前会长大大现场教学教的(我太菜了)...

刚看到这道题,想起以前做蓝桥杯的时候一道有关最小回文代价的题,题目翻译过来大概意思是就是给一个字符串,要求最少添加多少字符可以变为回文串,大概思路是对源串和逆串做LCS,源串长度再减去LCS的长度就是最小回文代价。

这道题刚开始我本来以为是源串和逆串求最长公共子串的,后来发现串长度太大了会超时,想了半天想不到什么降低复杂度的方法,网上一搜才知道原来有一种O(n)的算法叫manacher(马拉车算法)。

网上看了篇大佬的博客大致搞懂了,跟着大佬的思路写了遍代码....

大佬博客链接 点击打开链接

马拉车算法大概就是像KMP那样要求个Len数组,先在源字符串所有字符间以及首尾添加个#字符,为了避免越界在首尾加2个不同的其它字符比如@、*之类的。

然后遍历一遍,用right记录下之前找到的回文串最右端的边界(不包含),pos记录该回文串中点位置,每次i遍历时,判断i有没有超出right,没有的话Len[i]=min(Len(2*pos-i),right-i) (前者是i关于pos对称位置),超出的话Len[i]=1; 不过这个求出的Len[i]不一定是最终的,所以还需要一个while循环来更新

while(st[i-Len[i]] == st[i+Len[i]])    Len[i]++;  //st为变换后的字符串

然后更新下pos,right; 记录下最大的Len[i]为ans,ans-1就是要求的结果


#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
char ch1[120000];	//源串 
char ch2[300000];	//变换后的串 
int Len[300000];
int len,newLen;		//源串和新串长度 
void Init(){
	len = strlen(ch1);
	ch2[0] = '@';
	int pos = 0;
	for(int i = 1;i <= 2*len;i++){
		if(i&1)		//i是奇数
			ch2[i] = '#';
		else
			ch2[i] = ch1[pos++];		
	}
	ch2[2*len+1] = '#';
	ch2[2*len+2] = '*';
	ch2[2*len+3] = 0;
	newLen = 2 * len + 1;	
}
void Manacher(){
	int pos,right,ans;			
	pos = ans = Len[1] = 1;
	right = 2;
	for(int i = 2;i <= newLen;i++){
		int j = 2*pos-i;			//i关于pos的对称位置 		
		if(i < right)
			Len[i] = min(Len[j],right-i);
		else
			Len[i] = 1;
			//注意else后没有{} 
			while(ch2[i-Len[i]] == ch2[i+Len[i]])
				Len[i]++;			
			if(Len[i] + i > right) {			
				pos = i;
				right = i+Len[i];
			}				
		
		ans = max(ans,Len[i]);		
	}
	
	printf("%d\n",ans-1);
	
}



int main(){
	while(scanf("%s",ch1) != EOF) {
		Init();
		Manacher();
	}

	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值