专题·马拉车(manacher)【including 洛谷·【模板】manacher算法

初见安~:)

manacher

众所周知,马拉车算法是用来教马怎么拉车求最长回文字符串的一个均摊复杂度O(n)的算法

其原理也十分有趣,就是充分运用了之前处理出来的信息。

我们用数组maxlen[i]表示从i这个点往左右延伸的最大长度。就比如字符串abcba,【从1开始】,maxlen[3]=5。那如果是长度为偶数的回文串呢?我们的处理方式就是:在每个字符中间插入一个非字母字符【比如‘#】用以区分,然后照样跑即可。显然匹配时一定会在一个'#'匹配后停下,所以我们要保证字符串头尾也都是'#"。这样一来,我们要找的就全都是奇回文串了。

为了方便,我们让maxlen[i]的含义变为往右延伸的最大长度(包括i自己)。比如上面的例子,maxlen[3]=3。在插入了'#'字符后maxlen[3]=6,刚好是答案+1。

接下来我们看怎么跑。

假设前面有一个点mid作为中点,匹配回文串可以最远匹配到mx。

专题·马拉车(manacher)【including 洛谷·【模板】manacher算法_第1张图片

现在我们在点i的位置,关于mid对称有一个点j。

因为mid的对称性,我们首先可以判断i处的答案至少为min(maxlen(j),mx-i)。取min是因为j的匹配可能超出了mid-maxlen(mid),而这一部分并不和mx后面的部分相同。

但其实i可能还可以继续匹配。所以我们这时候可以暴力继续往后匹配。

还有一种情况是,i并不在mx以内。这时候我们没法利用前面的信息,初始化maxlen[i]=1,同样暴力往后匹配。

因为我们一直在更新mx,也就是说暴力匹配的话一定可以更新mx的大小,这样一来就最多匹配n次,复杂度\mathcal{O}(n)

模板题:洛谷P3805 【模板】manacher算法

#include
#include
#include
#include
#include
#include
#define maxl 11000010
using namespace std;
typedef long long ll;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

char s[maxl << 1];
int max_len[maxl << 1], len = 0;
signed main() {	
	char ch = getchar(); s[0] = '#';
	while(isalpha(ch)) s[++len] = ch, s[++len] = '#', ch = getchar();
	
	int ans = -1, mx = 0, mid = 0;
	for(int i = 1; i <= len; i++) {
		if(i < mx) max_len[i] = min(max_len[(mid << 1) - i], mx - i);//分情况
		else max_len[i] = 1;
		while(s[i - max_len[i]] == s[i + max_len[i]] && i + max_len[i] <= len && i >= max_len[i]) max_len[i]++;
		if(max_len[i] + i > mx) mx = max_len[i] + i, mid = i;//更新mx
		ans = max(ans, max_len[i] - 1);//注意答案要-1
	}
	printf("%d\n", ans);
	return 0;
}

迎评:)
——End——

你可能感兴趣的:(字符串,字符串,manacher)