Manacher算法

Manacher算法

可以早点吃饭吗?

不行喏,你这个函数都还没有写完

那我们什么时候吃饭?写完这个函数吗

哦不对.可是我们吃什么呢

虽然manacher不像unordered_map的unordered那么难打,但是我还是不想打,下文统一称为马拉车好了

朴素的方法计算一个字符串的最长子回文串长度就是枚举每一个索引i,计算以i为中心的左右同字符到底有多少对(偶数长字符串同理),但是试试上这并没有充分利用好回文串的性质.不废话直接上性质

  • 对于一个回文串,如果它的左边子串是一个回文串,那么它右边对称位置上也具有是相同长度的子回文串

那么我们就可以根据这个性质维护一个中心和右边界r,对于一个新的索引,如果它关于中心对称的那个位置上也具有回文串,我们可以直接置它为等长的回文串,但是这个位置上实际的最长回文串长度可能不止同对称位置那般短(如果超出右边界的话),但是我们可以再赋完值之后继续对这个位置进行朴素算法,如果更长的话我们也能省时间,同时更新中心位置和右边界.这样的复杂度其实没有很高

考虑到在同一个函数里面写马拉车要根据奇偶分类讨论,我们可以换种方式,在每个位置的之间和串左右边上加上'#',同时为了避免越界也可以进一步的在左右边界上加一对不对称的字符,比如$和*

这里贴上最终版代码和OW上的原始代码,看看吧

int mnc_mxpali(string s){
	string s1="$";
	for(int i=0;i<s.length();i++){
		s1=s1+'#';
		s1=s1+s[i];
	}
	s1=s1+"#*";
	vector<int>p(s1.length());
	int c=0,r=0;
	for(int i=1;i<s1.length();i++){
		if(i>r) p[i]=0;
		else p[i]=min(r-i,p[2*c-i]);
		while(s1[i-p[i]-1]==s1[i+p[i]+1]) p[i]++;
		if(p[i]+i>r){
			r=p[i]+i;
			c=i;
		}
	}
	int mx=-1;
	for(auto x:p){
		mx=max(x,mx);
	}
	return mx;
}
/*****************************************/
int d1[N],d2[N];

void mnc_odd(string s){
	int len=0;
	int n=s.length();
	for(int i=0,l=0,r=-1;i<n;i++){
		if(i>r) len=1;					//越过边界就置为1
		else len=min(d1[l+r-i],r-i+1);  //否则置为对称处的长,不越界
		while(i-len>=0&&i+len<n&&s[i-len]==s[i+len]) len++;
		d1[i]=len;
		len--;
		if(i+len>r){
			l=i-len;
			r=i+len;
		}
	}
}

void mnc_even(string s){//切记这个i是偏右的那个位置
						//也即i左边0.5格才是所算回文串的中心
	int len=0;
	int n=s.length();
	for(int i=0,l=0,r=-1;i<n;i++){
		if(i>r) len=0;
		else len=min(d2[l+r-i+1],r-i+1);
		while(i-len-1>=0&&i+len<n&&s[i-len-1]==s[i+len]) len++;
		d2[i]=len;
		len--;
		if(i+len>r){
			l=i-len-1;
			r=i+len;
		}
	}
}
posted @ 2025-10-11 19:09  江蝶  阅读(5)  评论(0)    收藏  举报