Codeup 问题 A: 【字符串】最长回文子串 manacher算法

原题地址

http://codeup.hustoj.com/problem.php?cid=100000629&pid=0

题目大意

输入一个字符串,求出其中最长的回文子串。在判断回文时,应该忽略所有标点符号和空格,且忽略大小写,但输出应保持原样(在回文串的首部和尾部不要输出多余字符)。输入字符串长度不超过 5000 5000 5000,且占据单独的一行。应该输出最长的回文串,如果有多个,输出起始位置最靠左的。

解题思路

一开始用dp的方法做,告诉我空间超限了,一看诶长度最大是 5000 5000 5000,而用dp的时候开的数组 d p [ m a x n ] [ m a x n ] dp[maxn][maxn] dp[maxn][maxn] 看来太大了,所以要换个办法~

后来去学了一下传说中找回文子串复杂度最优的算法——Manacher算法,复杂度为 O ( n ) O(n) O(n),AC本题。

找到必须看的一篇博客(俺也讲不清楚qwq):
必看!这篇看懂了!!hdu3068之manacher算法+详解

细节处理:读入字符串为 a l l all all,处理成全为小写字母且不带标点的 s s s,处理过程中用 p o s pos pos 数组记录 s s s 中每个字符对应在 a l l all all 中的位置,之后输出时知道 s s s 的左右端点就可以对应好位置,去 a l l all all 输出了。

注意事项

1.下标的转化。如经过Manacher算法预处理之后的字符串 s s s 长度是变长了,记录最长回文子串左端点的 l e f t left left 对应到预处理之前的 s s s,下标应该是 l e f t − 1 2 \frac {left-1}{2} 2left1

2.注意 p [ i ] p[i] p[i] 数组代表以 i i i 为中心的最长回文半径。因为是半径,所以可以算出子串的长度就是 p [ i ] − 1 p[i] - 1 p[i]1(需要认真阅读上面的博文,理解算法构造的原理之后自己推一下)。

参考代码

#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define LOCAL  //提交的时候一定注释
#define INF 0x3f3f3f3f
const int maxn = 6000;

int pos[maxn], p[maxn * 2];

int readint() {
    
    
    int x; scanf("%d", &x); return x;
}

int main() {
    
    
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
    // freopen("output.txt", "w", stdout);  
#endif
    string all, s = "";
    getline(cin, all);
    _for(i, 0, all.size()) {
    
    
        if (isalpha(all[i])) {
    
    
            pos[s.size()] = i;
            s += tolower(all[i]);
        }
    }
    int len = s.size(), ans = 0, id = 0, left;  //left记录左端点的值
    s += string(len + 4, '0');
    for (int i = len; i >= 0; --i) {
    
    
        s[i + i + 2] = s[i];
        s[i + i + 1] = '#';
    }
    s[0] = '*';
    _rep(i, 2, 2 * len) {
    
    
        if (p[id] + id > i)
            p[i] = min(p[2*id-i], p[id] + id - i);
        else p[i] = 1;
        while (s[i - p[i]] == s[i + p[i]]) ++p[i];
        if (p[i] + i > p[id] + id) id = i;
        if (p[i] > ans) {
    
    
            left = i - p[i] + 1;  //注意这里p[i]表示的是回文半径
            ans = p[i] ;
        }
    }
    _rep(i, pos[(left-1)/2], pos[(left-1)/2 + ans - 2]) printf("%c", all[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Encore47/article/details/109523763
今日推荐