manacher算法 洛谷P3805 P9606


前言

Manacher 算法,也称为马拉车算法,由美国计算机科学家 Glenn K. Manacher 在 1975 年提出,主要用于解决在给定字符串里查找最长回文子串的问题。相较于普通的暴力解法(时间复杂度为
O(n^2),Manacher 算法把时间复杂度优化到了O(n),效率显著提升。


一、模板

// 函数用于计算奇数和偶数长度回文子串的半径
vector<int> d1 ,d2;
string s;
void manacher() {
    int n = s.size();
    d1.resize(n);
    d2.resize(n);
    // 计算奇数长度回文子串半径 边界:(i-d1[i] , i+d1[i]) 
    for (int i = 0, l = 0, r = -1; i < n; ++i) {
        int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
        while (i - k >= 0 && i + k < n && s[i - k] == s[i + k]) ++k;
        d1[i] = --k;
        if (i + k > r) l = i - k, r = i + k;
    }
// 计算偶数长度回文子串半径 d[i]为中心两个的右边那个,所以d2[0]永远=-1 边界:(i-1-d1[i] , i+d1[i]) 
    for (int i = 0, l = 0, r = -1; i < n; ++i) {
        int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);
        while (i - k - 1 >= 0 && i + k < n && s[i - k - 1] == s[i + k]) ++k;
        d2[i] = --k;
        if (i + k > r) l = i - k - 1, r = i + k;
    }
}//max(max(d1)*2+1 , max(d2)*2+2)可得最长回文子串 

二、题目尝试

算法选择讲解:
1、传统的算法寻找最长回文子字符串,需要遍历每个字符,再基于单个字符向两边扩张,扩张时获取的数据信息再换下一个字符做扩张操作时不可用,造成资源浪费。
2、相比于同等时间复杂度和空间复杂度的字符串哈希求最大回文字符串的代码,代码实现难度较高
3、manacher的代码实现会方便很多

1.https://www.luogu.com.cn/problem/P3805

代码如下(示例):
这是一道纯模板题目,模板自己看一下,然后用这题试试,模板的使用看看有没有问题

2.https://www.luogu.com.cn/problem/P9606

代码如下(示例):
模板理解了,就可以试试这道,模板的d1数组和d2数组是重点,里面存储的数据要看懂

#include<bits/stdc++.h>
using namespace std;

// 函数用于计算奇数和偶数长度回文子串的半径
vector<int> d1 , d2;
string s;
void manacher() {
	int n = s.size();
	d1.resize(n);
	d2.resize(n);
	// 计算奇数长度回文子串半径 边界:(i-d1[i] , i+d1[i])
	for (int i = 0, l = 0, r = -1; i < n; ++i) {
		int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
		while (i - k >= 0 && i + k < n && s[i - k] == s[i + k]) ++k;
		d1[i] = --k;
		if (i + k > r) l = i - k, r = i + k;
	}
// 计算偶数长度回文子串半径 d[i]为中心两个的右边那个,所以d2[0]永远=-1 边界:(i-1-d1[i] , i+d1[i])
	for (int i = 0, l = 0, r = -1; i < n; ++i) {
		int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);
		while (i - k - 1 >= 0 && i + k < n && s[i - k - 1] == s[i + k]) ++k;
		d2[i] = --k;
		if (i + k > r) l = i - k - 1, r = i + k;
	}
}//max(max(d1)*2+1 , max(d2)*2+2)可得最长回文子串

int main() {
	int t;
	cin >> t >> s;
	manacher();
	int mmax1 = -1 , mmax2 = -1;
	for (int i = 0; i < (int)d1.size(); i++) {
		if (i + d1[i] == (int)d1.size() - 1 && d1[i] > mmax1) mmax1 = d1[i];
		if (i + d2[i] == (int)d2.size() - 1 && d2[i] > mmax2) mmax2 = d2[i];
	}

	if (mmax2 >= mmax1) {
		cout << t - (mmax2 * 2 + 2);
		return 0;
	} else {
		cout << t - (mmax1 * 2 + 1);
		return 0;
	}


	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值