manacher板子 记忆化求字符串每个位置的最长回文串的长度

本文介绍了ManachersAlgorithm在处理字符串中的回文问题,包括如何考虑奇偶长度,以及如何在代码中实现该算法。文章详细解释了算法原理,并提供了一个C++示例来计算以#分隔的字符串中最长回文串的长度。
摘要由CSDN通过智能技术生成

目录

原理

使用示范,本板子是加#(奇偶长度一起算)的:

 单独lamda:

OI Wiki摘录的只算单数和双数的:


原理

一句话:左边算过,右边可以直接(范围允许内)用。

r记录当前最右的回文(l(左)与之对应),这样我们后来在r中偏右进行判断时,因为l r之间是回文,所以可以参照中偏左对应的位置,少判断许多次。

使用示范,本板子是加#(奇偶长度一起算)的:

d[i]表示以位置i为中心的最长回文串的半径长度

d数组的值-1即是本位置最长回文长度,原因看最下面注释。

void solve()
{
	string str, aim;
	cin >> str;
	aim += "#";
	for (int i = 0; i < str.size(); i++)
	{
		aim += str[i];
		aim += "#";
	}

	vector<int>d(aim.size());

	//  abcba
	auto manacher = [&](string s) {
		int l = 0, r = -1;
		for (int i = 0; i < s.size(); i++)
		{
			int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界
			//k是半径,加了等于下一位
			//朴素算法
			while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])
				k++;
			d[i] = k;
			if (i + d[i] - 1 > r)
			{
				r = i + d[i] - 1;
				l = i - d[i] + 1;
			}
		}
	};
	manacher(aim);
	int ans = 0;
	for (auto x : d)
	{
		ans = max(ans, x);
	}
    //d[i]表示i(不包括i)左右的对称的长度
	// # a # b # a #
	//		 - - - -
	// # a # b # b # a #
	//		   - - - - -
	cout << ans - 1;
}

 单独lamda:



	//  abcba
	auto manacher = [&](string s) {
		int l = 0, r = -1;
		for (int i = 0; i < s.size(); i++)
		{
			int k = i > r ? 1 : min(d[l + r - i], r - i + 1);//左边对称相同,但不越界
			//k是半径,加了等于下一位
			//朴素算法
			while (i - k >= 0 && i + k <= s.size() && s[i - k] == s[i + k])
				k++;
			d[i] = k;
			if (i + d[i] - 1 > r)
			{
				r = i + d[i] - 1;
				l = i - d[i] + 1;
			}
		}
	};

————

OI Wiki摘录的只算单数和双数的:

vector<int> d1(n);
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 (0 <= i - k && i + k < n && s[i - k] == s[i + k]) {
    k++;
  }
  d1[i] = k--;
  if (i + k > r) {
    l = i - k;
    r = i + k;
  }
}
vector<int> d2(n);
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 (0 <= i - k - 1 && 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;
  }
}

练习:

3.反异或01串 - 蓝桥云课 (lanqiao.cn)

 其实是想随便做一道简单题备赛蓝桥,也没看标签。

蓝桥杯是不允许带纸质资料的,所以只能手搓板子。我忘了这个板子了,复习了下干什么的,搓完还是出错了(即右边用了左边的,但是超过当前右边界了。)

用了样例 101010 才发现。

 

参考代码:

向下取整同时处理了奇数回文和偶数回文,自己举例观察一下。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    //00111011

    //10 ^ 10 == 11
    //11000 操作后为 11011
    //

    //找最长回文串,但是这个长度应为1的数目

    //a#c#c#c#a
    //    ---
    //   ---
    //

    string str;
    cin >> str;
    string aim;
    aim += ' ';
    aim += str[0];
    for (int i = 1; i < str.size(); i++)
    {
        aim += '#';
        aim += str[i];
    }
    vector<int>pre(aim.size());
    int cnt1 = 0;
    for (int i = 1; i < aim.size(); i++)
    {
        if (aim[i] == '1')
        {
            pre[i]++;
            cnt1++;
        }
        pre[i] += pre[i - 1];
    }

    int maxn = 0;
    int n = aim.size();
    vector<int>manacher(n);
    int mr = 1, mi = 1;
    for (int i = 1; i < n; i++)
    {
        int k = 1;
        if (i < mr)
        {
            //k = manacher[mi-(mr-mi)+mr-i];
            k = min(mr-i,manacher[mi-(i-mi)]);
        }

        while (0 < i - k && i + k < n)
        {
            if (aim[i - k] == aim[i + k])
            {
                k++;
            }
            else
                break;
        }
        k--;
        manacher[i] = k;

        if (i + k > mr)
        {
            mr = i + k;
            mi = i;
        }
        maxn = max(maxn, pre[i + k] - pre[i - k - 1]);
    }

    cout << cnt1 - maxn / 2 << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值