P4287 [SHOI2011]双倍回文(PAM(回文自动机/回文树),manacher)

传送门
这题似乎用manacher可做而且很快,但用PAM做可能更好理解.
看了题解都是用fail指针来写这题,但其实可以不用那么麻烦.
考虑一下双倍回文的性质.
1.首先肯定是一个回文字符串.
2.从中间分开两边都是回文串.
3.长度是4的倍数.
暴力:第一个性质保证了它一定在PAM的一个节点上,我们可以最暴力的对每个节点判断是否是双倍回文.这种做法就不提了.
正解:双倍回文串在PAM上的特点是它的转移字符形成的字符串一定是回文串.比如说abbaabba. 在PAM上它是这么形成的:a->b->b->a.可以发现这个转移字符形成的字符串同样是回文的.所以我们根据这个特点来判断就ok了.可以再套一个PAM或者直接用manacher判断.
注意不能对每个节点都判断一次,而是在每个叶子节点跑一遍PAM或者manacher判断最长的回文串就好了.这回文串一定要是1-i形成的回文串.
PS:这个代码虽然又长又慢,但是思路应该是很清晰的.
代码:

#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>

using namespace std;
const int N = 5e5+10;

inline int read(){
    
    
    int x = 0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace man{
    
    
    int p[N*3],n;
    void getstr(string str,int &ans){
    
    
        n = str.size();
        string newstr = "";
        newstr += "&";newstr += "#";
        for(auto x:str) newstr += x,newstr += "#";
        newstr += "*";
        str = newstr;
        int id = 0,mx = 0;
        fir(i,0,(int)str.size()-2)
            p[i] = 0;
        fir(i,1,(int)str.size()-2){
    
    
            if(i < mx) p[i] = min(p[2*id-i],mx-i);
            else p[i] = 1;
            while(str[i-p[i]] == str[i+p[i]]) p[i]++;
            if(i + p[i] > mx) id = i,mx = i+p[i];
            int pos = i/2;
            if((p[i]-1)*2%4==0 && (pos*2 == p[i]-1)) ans = max(ans,(p[i]-1)*2);
        }
    }
}
namespace PAM{
    
     // 双倍回文条件 :  1.在PAM上面的路径字符串是回文的  2.路径长度*2 % 4 == 0
    int nxt[26][N],S[N],fail[N],num[N],last,n,tot,ans;
    int cnt[N],len[N];
    int newnode(int l){
    
    
        len[tot] = l,cnt[tot] = num[tot] = 0;fir(i,0,25) nxt[i][tot] = 0;return tot++;
    }
    void init(){
    
    
        last = n = tot = 0;newnode(0),newnode(-1);fail[0] = 1;S[0] = -1;
    }
    int getfail(int x){
    
    
        while(S[n-len[x]-1] != S[n])
            x = fail[x];
        return x;
    }
    void add(int ch){
    
    
        ch -= 'a';
        S[++n] = ch;
        int cur = getfail(last);
        if(!nxt[ch][cur]){
    
    
            int now = newnode(len[cur]+2);
            fail[now] = nxt[ch][getfail(fail[cur])];
            nxt[ch][cur] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = nxt[ch][cur];
        cnt[last]++;
    }
    string s = "";
    void dfs(int p){
    
    
        bool f = 1;
        int siz = s.size();
        fir(i,0,25){
    
    
            if(nxt[i][p]){
    
    
                s += ('a'+i);
                dfs(nxt[i][p]);
                f = 0;
                s.resize(siz);
            }
        }
        if(f){
    
    
            man::getstr(s,ans);
        }
    }
}
int main(){
    
    
    int n;
    string s;
    cin >> n >> s;
    PAM::init();
    for(auto x:s) PAM::add(x);
    PAM::dfs(0);
    cout << PAM::ans << "\n";
    
    return 0;
}    
/* 1
aaaaaabcddcbaabcddcbaaaaaa
  16
*/

猜你喜欢

转载自blog.csdn.net/weixin_45590210/article/details/109850771