L2-008 最长对称子串 (回文子串 / DP / Manacher算法)

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

Is PAT&TAP symmetric?

输出样例:

11

思路一

由于字符串长仅1000,可以选择直接暴力找

思路二

套一下马拉车算法的模板即可

算法讲解:Here

Code Update:更新算法写法

// Murabito-B 21/04/23
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
string Manacher(string s) {
    int len = s.size();
    if (len < 1) return s;
    string ss;
    // 预处理
    for (int i = 0; i < len; ++i) {
        ss += '#';
        ss += s[i];
    }
    ss += '#';
    len        = ss.size();
    int MaxR   = 0;            // 当前访问到的所有回文子串,所能触及的最右一个字符的位置
    int pos    = 0;            // MaxRight对应的回文串的对称轴所在的位置
    int MaxRL  = 0;            // 最大回文串的回文半径
    int MaxPos = 0;            // MaxRL对应的回文串的对称轴所在的位置
    int *RL    = new int[len]; // RL[i]表示以第i个字符为对称轴的回文串的回文半径
    memset(RL, 0, len * sizeof(int));
    for (int i = 0; i < len; ++i) {
        //1) 当i在MaxRight的左边
        if (i < MaxR) RL[i] = min(RL[2 * pos - 1], MaxR - i);
        else // 2) 当i在MaxRight的右边
            RL[i] = 1;

        while (i - RL[i] >= 0 && i + RL[i] < len && ss[i - RL[i]] == ss[i + RL[i]])
            RL[i]++;

        // 更新MaxRight, pos
        if (RL[i] + i - 1 > MaxR) {
            MaxR = RL[i] + i - 1;
            pos  = i;
        }
        // 更新MaxRL, MaxPos
        if (MaxRL <= RL[i]) {
            MaxRL  = RL[i];
            MaxPos = i;
        }
    }
    return s.substr((MaxPos - MaxRL + 1) / 2, MaxRL - 1);
}
void solve() {
    string s;
    getline(cin, s);
    cout << Manacher(s).size() << "\n";
}
int main() {
    ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
string s;
char s_new[4000];
int p[5000];
int Init() {
    int len = s.length();
    s_new[0] = '$';
    s_new[1] = '#';
    int j = 2;
    for (int i = 0; i < len; i++) {
        s_new[j++] = s[i];
        s_new[j++] = '#';
    }
    s_new[j] = '\0';  // 别忘了哦
    return j;         // 返回 s_new 的长度
}

int Manacher() {
    int len = Init();  // 取得新字符串长度并完成向 s_new 的转换
    int max_len = -1;  // 最长回文长度
    int id;
    int mx = 0;
    for (int i = 1; i < len; i++) {
        if (i < mx)
            p[i] = min(p[2 * id - i],
                       mx - i);  // 需搞清楚上面那张图含义, mx 和 2*id-i 的含义
        else
            p[i] = 1;

        while (s_new[i - p[i]] ==
               s_new[i + p[i]])  // 不需边界判断,因为左有'$',右有'\0'
            p[i]++;

        // 我们每走一步 i,都要和 mx 比较,我们希望 mx
        // 尽可能的远,这样才能更有机会执行 if (i < mx)这句代码,从而提高效率
        if (mx < i + p[i]) {
            id = i;
            mx = i + p[i];
        }
        max_len = max(max_len, p[i] - 1);
    }
    return max_len;
}

int main() {
    getline(cin, s);
    printf("%d\n", Manacher());
    return 0;
}

思路三

没想到可以使用DP做,思路来自网络

动态规划:

分析:有两种可能,⼀种是回⽂字符串的⻓度为奇数,⼀种是偶数的情况。i为字符串当前字符的下 标。

     当回⽂字串为奇数的时候,j表示i-j与i+j构成的回⽂字串⻓度;当回⽂字串⻓度为偶数的时候,j表示

     i+1左边j个字符⼀直到i右边j个字符的回⽂字串⻓度~

     ⽤maxvalue保存遍历结果得到的最⼤值并且输出~
#include <iostream>
using namespace std;
int main() {
    string s;
    getline(cin, s);
    int maxvalue = 0, temp;
    int len = s.length();
    for (int i = 0; i < len; i++) {
        temp = 1;
        for (int j = 1; j < len; j++) {
            if (i - j < 0 || i + j >= len || s[i - j] != s[i + j])
                break;
            temp += 2;
        }
        maxvalue = temp > maxvalue ? temp : maxvalue;
        temp = 0;
        for (int j = 1; j < len; j++) {
            if (i - j + 1 < 0 || i + j >= len || s[i - j + 1] != s[i + j])
                break;
            temp += 2;
        }
        maxvalue = temp > maxvalue ? temp : maxvalue;
    }
    cout << maxvalue;
    return 0;
}
posted @ 2020-11-26 20:40  RioTian  阅读(184)  评论(0编辑  收藏  举报