codeforces 1326 d2 Prefix-Suffix Palindrome (回文,KMP算法)

题目大意:

已知长度为n的字符串str,我们从中抽出a,b。其中满足:

a是字符串的前缀,b是字符串的后缀。a+b是回文串。

n<=1e5

解题思路:

首先它是回文串,所以我们有一个观察,答案必定是这样构成的:

在这里不失一般性,我们假设后缀是比较长的。

同时我们发现,黑色部分的回文一定是贪心取最长的,不存在取短一点更优的情况。所以,在这里我们就变为了怎么求最长的红色的回文串。这里需要用到kmp算法。我们在原字符串中去掉黑色回文串后得到last.

然后构造新字符串:last + '$' + reverse(last),其中$是肯定不在last的一个字符。reverse代表字符串反转。

跑kmp得出的长度就是最佳前缀回文串的长度。要求最佳后缀回文串的话,只需要把last反转即可。

为什么可以这样做呢?因为kmp算法是可以O(N)得到在这个字符串中既是前缀也是后缀的最长的字符串。

加入这个特殊字符是为了防止越界,得到比原串更长的回文。至于为什么需要reverse,因为回文本质上就是正着读和反着读一样,所以reverse之后我们利用kmp找最长的既是前缀也是后缀这个特性,我们就能把回文找出来,可以在纸上画一下就知道了。

#include <bits/stdc++.h>
using namespace std;
const int M = (int)(2e6 + 239);
 
int pref[M];
string solve_palindrome(const string& s)
{
    string a = s;
    reverse(a.begin(), a.end());
    a = s + "#" + a;
    int c = 0;
    for (int i = 1; i < (int)a.size(); i++)
    {
        while (c != 0 && a[c] != a[i])
            c = pref[c - 1];
        if (a[c] == a[i])
            c++;
        pref[i] = c;
    }
    return s.substr(0, c);
}
int main(){
    int cas;cin>>cas;
    while(cas--){
        string str;cin>>str;
        int n=str.size();
        int l=0,r=n-1;
        while(l<r && str[l]==str[r])l++,r--;
        string ansmid("");
        if(r-l+1>0){
        string s=str.substr(l,(r-l+1));
        ansmid=solve_palindrome(s);
        reverse(s.begin(),s.end());
        if(solve_palindrome(s).size()>ansmid.size())ansmid=solve_palindrome(s);
        }
        string ans1=str.substr(0,l);
        string ans2=ans1;
        reverse(ans2.begin(),ans2.end());
        cout<<ans1<<ansmid<<ans2<<endl;
    }
	return 0;
}
发布了171 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/FrostMonarch/article/details/105147850