【字符串】回文串专题(中心扩展法、manacher马拉车算法)

数据量较大时只能用manacher算法,但一般面试不会考到这么深,Leetcode上的题中心扩展法就可以解决。

中心扩展法 O(n2)

枚举以每个字符为中心,向左右两边扩展的最长回文串长度

求最长回文串的长度

注意,回文串的长度只可能是奇数或者偶数
长度为奇数时,传入work(s, i, i),表示左右起点均为i
长度为偶数时,传入work(s, i, i + 1),表示左起点为i,右起点为i+1

int work(string& s, int l, int r)
{  // 以给定位置为中心向左右扩展,返回最长回文串长度
    int n = s.size();
    while (l >= 0 && r < n && s[l] == s[r])
    {
        l--, r++;
    }
    // l,r停留在第一个不匹配的位置,实际的回文串是从l+1到r-1
    // 所以回文串的长度是(r-1)-(l+1)+1 = r - l - 1
    return r - l - 1;
}

5. 最长回文子串 - 力扣(LeetCode)

本题不需要求出最长回文串的右边界r,但是这里为了更清晰写上了。

class Solution {
public:
    int work(string &s, int l, int r)
    {
        int n = s.size();
        while (l >= 0 && r < n && s[l] == s[r])
        {
            l--, r++;
        }
        return r - l - 1;
    }
    string longestPalindrome(string s) {
        int n = s.size();
        int l = 0, r = 0, max_len = 0;
        for (int i = 0; i < n; i++) 
        {
            int len1 = work(s, i, i);
            int len2 = work(s, i, i + 1);
            if (max_len < len1) 
            {
                max_len = len1;
                l = i - len1 / 2;
                r = i + len1 / 2;
            }
            if (max_len < len2)
            {
                max_len = len2;
                l = i - (len2 - 1) / 2;
                r = i + len2 / 2;
            }
        }
        return s.substr(l, max_len);
    }
};

P1210 [USACO1.3] 最长的回文 Calf Flac

image

image

image

#include <iostream>
#include <vector>

using namespace std;

int work(string& s, int l, int r)
{  // 以给定位置为中心向左右扩展,返回最长回文串长度
    int n = s.size();
    while (l >= 0 && r < n && s[l] == s[r])
    {
        l--, r++;
    }
    // l,r停留在第一个不匹配的位置,实际的回文串是从l+1到r-1
    // 所以回文串的长度是(r-1)-(l+1)+1 = r - l - 1
    return r - l - 1;
}

void solve()
{
    string s, line;
    while (getline(cin, line))
    {
        s += line + '\n';
    }

    string new_s;
    vector<int> pos;
    for (int i = 0; i < s.size(); i++)
    {
        if (isalpha(s[i]))
        {
            new_s += tolower(s[i]);
            pos.push_back(i);
        }
    }
    int st = 0, ed = 0, max_len = 0;
    for (int i = 0; i < new_s.size(); i++)
    {
        int len1 = work(new_s, i, i);      // 奇数,左右起点都是i
        int len2 = work(new_s, i, i + 1);  // 偶数,左起点是i,右起点是i+1
        if (len1 > max_len)
        {
            max_len = len1;
            st = i - len1 / 2;
            ed = i + len1 / 2;
        }
        if (len2 > max_len)
        {
            max_len = len2;
            st = i - (len2 - 1) / 2;
            ed = i + len2 / 2;
        }
    }
    string res = s.substr(pos[st], pos[ed] - pos[st] + 1);
    cout << max_len << '\n' << res;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

求回文子串的个数

常用函数

    int cal(string &s, int l, int r)//计算以[l,r]为中心的回文串的数量
    {
        int n = s.size(), cnt = 0; //cnt记录回文串的个数
        while (l >= 0 && r < n && s[l] == s[r])
        {
            l--, r++;
            cnt++;
        }
        return cnt;
    }

647. 回文子串 - 力扣(LeetCode)

class Solution {
public:
    int cal(string &s, int l, int r)//计算以[l,r]为中心的回文串的数量
    {
        int n = s.size(), cnt = 0; //cnt记录回文串的个数
        while (l >= 0 && r < n && s[l] == s[r])
        {
            l--, r++;
            cnt++;
        }
        return cnt;
    }
    int countSubstrings(string s) {
        int n = s.size();
        int res = 0;
        for (int i = 0; i < n; i++) 
        {
            res += cal(s, i, i);//奇数个字符,以i为中心
            res += cal(s, i, i + 1);//偶数个字符,以i,i+1为中心
        }
        return res;
    }
};

Manacher算法 O(n)

学习资料
1.F05 Manacher(马拉车)

2.算法讲解103【扩展】 Manacher算法、扩展KMP


image

代码模板

string a;  // 原串
string s;  // 改造串
int d[N];  // 回文半径函数
int n;     // 改造串长度(不含哨兵)

void get_d(string& s, int n)
{
    d[1] = 1;
    for (int i = 2, l, r = 1; i <= n; i++)
    {  // r取0或1都可,因为第一轮循环if都不会执行
        if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
        while (s[i - d[i]] == s[i + d[i]]) d[i]++;
        if (i + d[i] - 1 > r) l = i - (d[i] - 1), r = i + d[i] - 1;
    }
}

int init()
{
    cin >> a;
    n = a.size();
    a = ' ' + a;  // 字符串下标从1开始
    s = "$#";     // 初始化改造串
    for (int i = 1; i <= n; i++) s += a[i], s += '#';
    n = s.size() - 1;  // 改造串长度(不含哨兵)

    get_d(s, n);  // 求最长半径
    int res = 0;
    for (int i = 1; i <= n; i++) res = max(res, d[i]);
    return res - 1;
}

P3805 【模板】manacher

string版本

#include <iostream>

using namespace std;

const int N = 2.2e7 + 10;

string a;  // 原串
string s;  // 改造串
int d[N];  // 回文半径函数
int n;

void get_d(string& s, int n)
{
    d[1] = 1;
    for (int i = 2, l, r = 1; i <= n; i++)
    {  // r取0或1都可,因为第一轮循环if都不会执行
        if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
        while (s[i - d[i]] == s[i + d[i]]) d[i]++;
        if (i + d[i] - 1 > r) l = i - (d[i] - 1), r = i + d[i] - 1;
    }
}

void init()
{
    cin >> a;
    n = a.size();
    a = ' ' + a;  // 字符串下标从1开始
    s = "$#";     // 初始化改造串
    for (int i = 1; i <= n; i++) s += a[i], s += '#';
    n = s.size() - 1;  // 改造串长度(不含哨兵)
}

void solve()
{
    init();       // 预处理
    get_d(s, n);  // 求最长半径
    int res = 0;
    for (int i = 1; i <= n; i++) res = max(res, d[i]);
    cout << res - 1;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    while (T--) solve();
    return 0;
}

char*版本

#include <cstring>
#include <iostream>

using namespace std;

const int N = 2.2e7;
char a[N], s[N];
int d[N];  // 回文半径函数

void get_d(char* s, int n)
{
    d[1] = 1;
    for (int i = 2, l, r = 1; i <= n; i++)
    {
        if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
        while (s[i - d[i]] == s[i + d[i]]) d[i]++;
        if (i + d[i] - 1 > r) l = i - d[i] + 1, r = i + d[i] - 1;
    }
}
int main()
{
    // 改造串
    scanf("%s", a + 1);
    int n = strlen(a + 1), k = 0;
    s[0] = '$', s[++k] = '#';
    for (int i = 1; i <= n; i++) s[++k] = a[i], s[++k] = '#';
    n = k;

    get_d(s, n);  // 计算d函数
    int ans = 0;
    for (int i = 1; i <= n; i++) ans = max(ans, d[i]);
    printf("%d\n", ans - 1);
    return 0;
}
posted @   Tshaxz  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
Language: HTML
点击右上角即可分享
微信分享提示