Manacher最长回文串匹配

复习又学习了两遍。

Manacher讲究以线性时间复杂度解决寻找字符串中的最长回文串。

1.将字符串预处理,统一了奇数偶数长度的字符串——在字符串中给字符添加分隔符。

这里是将s处理后得到S字符串。

void init(string s)
{
    S[0] = '@';
    int l = s.length();
    for (int i = 1; i <= l * 2; i += 2)
    {
        S[i] = '#';
        S[i + 1] = s[i / 2];
    }
    S[l * 2 + 1] = '#';
}
View Code

2.一个数组p[i]表示以i位置为中心的回文串最长长度。

这个数组有一个性质,p[i]-1即表示了原串回文串的长度

证明如下:

首先在所有转换后的字符串S中,所有回文串长度都为奇数

也即,对于p[i]为中心的最长回文串,其长度就是p[i]*2-1

又因为分隔符的数量一定比原字符多1,也就是有p[i]个分隔符,剩下p[i]-1个字符是原字符串

所以该回文串在原字符串的长度就是p[i]-1

 

 

现在的任务就是一步一步将p[i]给从头处理到尾(从左到右)即可得到结果。

 

3.p[]数组的计算,需要变量【id表示当前已计算的最长回文串中心,mx表示当前已计算的最长回文串的最右端下标】

下面分两个部分,一个是在已计算区间找p,一个是未计算区间找p

(为什么这么说?不是都要计算的吗?答:利用对称性,当已得到的最长回文串的最右端mx大于当前遍历中心i,那么一定且肯定当前p[i]可以省略计算)

3.1已计算区间找p

也即mx>i,很简单,现在的p[i]是 已知回文串的一半 的 一个部分,那么直接去找 另一半 的 对应部分 的p即可,

也即p[i]=p[id-(i-id)]

需要注意的是:有可能 这个对称部分 不完全包括在 那一个已知的回文串中

也就是需要考虑极限对称长度mx-i即可

总结就是p[i]=min(p[id-(i-id)],mx-i)

3.2未计算区间找p

p[i]=1,然后直接也只能暴力匹配——【while(S[i-p[i]==S[i+p[i])++p[i]】

当更新到i+p[i]>mx时候要更新mx和id

 

如上循环结束匹配即Manacher

示范代码:

 

#include <bits/stdc++.h>
using namespace std;
#define N 202200
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define cn(s) cout << s << endl
typedef long long ll;
int p[N * 2];
string S;
void init(string s)
{
    S[0] = '@';
    int l = s.length();
    for (int i = 1; i <= l * 2; i += 2)
    {
        S[i] = '#';
        S[i + 1] = s[i / 2];
    }
    S[l * 2 + 1] = '#';
}
void solve(string s)
{
    init(s);
    int mx = 0, id = 0, l = s.length() * 2, ans = 0;
    for (int i = 0; i < l; ++i)
    {
        if (mx > i)
            p[i] = min(p[id - i + id], mx - i);
        else
            p[i] = 1;
        while (S[i - p[i]] == S[i + p[i]])
            ++p[i];
        if (p[i] + i > mx)
        {
            mx = p[i] + i;
            id = i;
        }
        ans = max(ans, p[i]);
    }
    cn(ans - 1);
}
int main()
{
    IOS;
    string s;
    while (cin >> s)
        solve(s);
    return 0;
}
View Code

 

 C++,version:

class Solution
{
    string S, ans;
    void Init(string s)
    {
        S = "~#";
        for (auto i : s)
            S = S + i + '#';
    }
    string manacher(string s, string fs)
    {
        int mx = 0, id = 0, l = s.length(), res = 0, start = 0;
        vector<int> p(l);
        for (int i = 1; i < l; ++i)
        {
            if (mx > i)
                p[i] = min(mx - i, p[id - (i - id)]);
            else
                p[i] = 1;
            while (s[i - p[i]] == s[i + p[i]])
                ++p[i];
            if (p[i] + i > mx)
            {
                mx = p[i] + i;
                id = i;
            }
            if (p[i] > res)
            {
                res = p[i];
                // ans = s.substr(id - p[i] + 1, 2 * p[i] - 1);
                ans = fs.substr((id - p[i]) / 2, p[i] - 1);
            }
        }
        return ans;
    }

public:
    string longestPalindrome(string s)
    {
        Init(s);
        return manacher(S, s);
    }
};
standard

 

学习博客:https://blog.csdn.net/dyx404514/article/details/42061017

posted @ 2022-02-13 16:20  Renhr  阅读(31)  评论(0)    收藏  举报