luogu P4287 [SHOI2011]双倍回文(PAM回文自动机,trans指针,倍增优化)

题目链接:https://www.luogu.com.cn/problem/P4287

题意:给定一个长为n(n<=5e5)的字符串,问最长的满足AA'AA'的子串长度。A'为A的反转串如abb->bba

如:16   ggabaabaabaaball

答案:12

题解:回文自动机记录fail以及len。然后再len[i]%4==0时找

是否fail路径上有len[p]*2==len[i],如果有则更新ans=max(ans,len[i])。必然会超时emmm。

trans指针倍增优化???看懂了。

trans[i]指向小于等于len[i]/2的最长回文后缀(树上一个节点代表一个回文,对应字母表示回文的最后一个字母)。

求法:当新建立一个节点,如果它的fail回文长度<=1/2倍len[tot]回文长度,那么f[tot]=fail[tot]。

否则,从它的父亲开始跳一直跳到小于等于1/2len[tot]。最后f[tot]=tree[p][t]。理解还不透彻,见代码:

代码:

#include <bits/stdc++.h>

#define ll int
#define ld double
#define pi acos(-1)
#define pb push_back
#define mst(a, i) memset(a, i, sizeof(a))
#define pll pair<ll, ll>
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define rep(i,a,n)  for(ll i=a;i<=n;i++)
#define per(i,n,a)  for(ll i=n;i>=a;i--)
#define dbg(x) cout << #x << "===" << x << endl
#define dbgg(l,r,x) for(ll i=l;i<=r;i++) cout<<x[i]<<" ";cout<<"<<<"<<#x;cout<<endl
using namespace std;

template<class T>void read(T &x){T res=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){res=(res<<3)+(res<<1)+c-'0';c=getchar();}x=res*f;}
inline void print(ll x){if(x<0){putchar('-');x=-x;}if(x>9)print(x/10);putchar(x%10+'0');}
const ll maxn = 5e5 + 10;
const ll mod = 1e9+7;

ll n,len[maxn],fail[maxn],f[maxn],tree[maxn][30];
char c[maxn];
ll getfail(ll x,ll i){
    while(c[i]!=c[i-len[x]-1]) x=fail[x];
    return x;
}
//ll inv(ll a){return a==1?1:(ll)(mod-mod/a)*inv(mod%a)%mod;}
//ll gcd(ll a,ll b){return (b==0)?a:gcd(b,a%b);}
//ll qpow(ll a,ll p,ll mod){if(p<=0) return 1;ll ans=1;a=a%mod;while(p){if(p&1)ans=(ans*a)%mod;p>>=1;a=(a*a)%mod;}return ans;}
//void build(ll p,ll l,ll r){if(l==r){sum[p]=a[l];return ;}ll mid=(l+r)>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);sum[p]=sum[p<<1]+sum[p<<1|1];}
//void pushdown(ll p,ll l,ll r){ll mid=(l+r)>>1;sum[p<<1]+=(mid-l+1)*tag[p];sum[p<<1|1]+=(r-(mid+1)+1)*tag[p];tag[p<<1]+=tag[p];tag[p<<1|1]+=tag[p];tag[p]=0;}
//void update(ll p,ll l,ll r,ll x,ll y,ll k){if(x<=l&&r<=y){sum[p]+=(r-l+1)*k;tag[p]+=k;return ;}pushdown(p,l,r);ll mid=(l+r)>>1;if(x<=mid) update(p<<1,l,mid,x,y,k);if(y>mid) update(p<<1|1,mid+1,r,x,y,k);sum[p]=sum[p<<1]+sum[p<<1|1];}
//ll query(ll p,ll l,ll r,ll x,ll y){ll res=0;if(x<=l&&r<=y) return sum[p];ll mid=(l+r)>>1;pushdown(p,l,r);if(x<=mid) res+=query(p<<1,l,mid,x,y);if(y>mid) res+=query(p<<1|1,mid+1,r,x,y);return res;}
int main() {
    ll _s = 1;
    // read(_s);
    //freopen("testdata.in","r",stdin);
	//freopen("testout.out","w",stdout);
    for (ll _=1;_<=_s;_++) {
        read(n);
        scanf("%s",c+1);//?c+1这样不可能i-len[x]-1=-1;
        len[1]=-1,fail[0]=1;
        ll cur,lst=0,t,tot=1;//?lst要先0后1
        rep(i,1,n){
            cur=getfail(lst,i);t=c[i]-'a';
            if(!tree[cur][t]){
                fail[++tot]=tree[getfail(fail[cur],i)][t];//?fail值咋个更新的?
                //?是这样吗?cur的上一位满足的
                len[tot]=len[cur]+2;
                tree[cur][t]=tot;
                //?错误示例:if(len[cur]<=len[tot]/2) f[tot]=fail[tot];
                if(len[fail[tot]]<=len[tot]/2) f[tot]=fail[tot];
                else{
                    //?错误示例:ll p=fail[cur];
                    ll p=f[cur];
                    while(len[p]+2>len[tot]/2||c[i]!=c[i-len[p]-1]) p=fail[p];
                    //?错误示例:f[tot]=p;
                    f[tot]=tree[p][t];//?儿子
                }
            }
            //?错误示例:lst=tot;//?哪一个是最长后缀?
            lst=tree[cur][t];
        }
        // dbgg(1,n,fail);
        // dbgg(1,n,f);
        ll ans=0;
        //?下标也已经变了
        rep(i,2,tot){
            if(len[i]%4==0&&len[i]==2*len[f[i]]) ans=max(ans,len[i]);
            // if(len[i]%4!=0) continue;
            // ll p=fail[i];
            // while(len[p]*2!=len[i]&&p!=1) p=fail[p];
            // ans=max(ans,len[i]);
        }
        cout<<ans;

    }
    return 0;
}
//?以上就是我自己打的一份代码,都有什么问题,一目了然
/*
input:::

output:::

*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值