AcWing 139 回文子串的最大长度 (哈希+二分 / Manacher)

https://www.acwing.com/problem/content/141/

哈希做法:
对于每个回文串,长度要么是奇数,要么是偶数
如果长度为奇数,那么枚举中间点,二分长度,用哈希判断左右字串是否相等
如果为偶数,则枚举中间空格

预处理出前缀和后缀字串的哈希值,则可以在(O(1))时间内求出任何字串的哈希值

Manacher:
Manacher算法的核心思想就是维护每个位置的回文半径(p[i]),然后维护前面已经
处理过的位置中,回文半径扩展到的最右端的位置 (r) ,和扩展到 (r) 位置的回文串中心位置 (mid),

于是对当前位置,如果当前位置在(r)的范围之内,则可以先将答案更新成对称位置处的长度,
然后再暴力扩展。 所有位置暴力扩展的长度之和是(n),所以Manacher是线性做法

哈希+二分:时间复杂度(O(nlogn))

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 1000010;
const int M = 1000000007;

int n, ans;
int base = 131;
int Hash_pre[maxn], Hash_suf[maxn], P[maxn];
char s[maxn];

bool Check_odd(int x, int pos){
	if(pos <= x || pos + x > n) return false;
	
	int Hl = (Hash_pre[pos - 1] - 1ll * Hash_pre[pos - x - 1] * P[x] % M + M) % M;
	int Hr = (Hash_suf[pos + 1] - 1ll * Hash_suf[pos + x + 1] * P[x] % M + M) % M;
	
	if(Hl == Hr) return true;
	else return false;
}

bool Check_even(int x, int pos){
	if(pos < x || pos + x > n) return false;
	
	int Hl = (Hash_pre[pos] - 1ll * Hash_pre[pos - x] * P[x] % M + M) % M;
	int Hr = (Hash_suf[pos + 1] - 1ll * Hash_suf[pos + x + 1] * P[x] % M + M) % M;
	
	if(Hl == Hr) return true;
	else return false;
}

void erf(int pos){
	int res = 0;
	// odd erf
	int L = 0, R = n;
	while(L < R){
		int Mid = (L + R + 1) >> 1;
		if(Check_odd(Mid, pos)){
			L = Mid;
		} else {
			R = Mid - 1;
		}
	}
	res = L;
	ans = max(ans, res * 2 + 1);
	// even erf
	
	L = 0, R = n;
	while(L < R){
		int Mid = (L + R + 1) >> 1;
		if(Check_even(Mid, pos)){
			L = Mid;
		} else{
			R = Mid - 1;
		}
	}
	res = L;
	ans = max(ans, res * 2);
} 

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	int Case = 0;
	while(scanf("%s",s+1) && s[1] != 'E'){
		ans = 0;
		n = strlen(s+1);
		
		P[0] = 1;
		for(int i=1;i<=n;++i){
			Hash_pre[i] = (1ll * Hash_pre[i-1] * base % M + (s[i] - 'a' + 1)) % M;
			P[i] = 1ll * P[i-1] * base % M;
		}
			
		for(int i=n;i>=1;--i)
			Hash_suf[i] = (1ll * Hash_suf[i+1] * base % M + (s[i] - 'a' + 1)) % M;
		
		for(int i=1;i<=n;++i){
			erf(i);	
		}
		
		printf("Case %d: %d
", ++Case, ans);
	}
	return 0;
}

Manacher:(O(n))

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 2200010;

int n, len, ans;
int p[maxn], mid, r;
char s[maxn],ss[maxn];

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	int Case = 0;
	while(scanf("%s",s+1) && s[1] != 'E'){
		len = 0; mid = 0, r = 0; ans = 0;
		memset(p, 0 , sizeof(p));
		n = strlen(s+1);
		
		ss[0] = '~';
		for(int i=1;i<=n;++i){
			ss[++len] = '#';
			ss[++len] = s[i];
		}
		ss[++len] = '#';
		for(int i=0;i<=len;++i) s[i] = ss[i];
		
		for(int i=1;i<=len;++i){
			if(i <= r) p[i] = min(p[mid * 2 - i], r - i + 1);
			
			while(s[p[i] + i] == s[i - p[i]]) ++p[i];
			
			if(p[i] + i - 1 > r){
				r = p[i] + i - 1, mid = i;
			}
			ans = max(ans, p[i] - 1);
		}
		
		printf("Case %d: %d
",++Case,ans);
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/13940484.html