bzoj3676 [Apio2014]回文串 卡常+SAM+树上倍增

bzoj3676 [Apio2014]回文串 SAM+树上倍增

链接

bzoj
luogu

思路

根据manacher可以知道,每次暴力扩展才有可能出现新的回文串。
所以推出本质不同的回文串个数是O(n)级别的。
每次查询一个串出现的个数。
建立出parent树,一个串出现的个数就是对应parent树上的所在的子树的大小,利用树上倍增可以log求出一个串出现的个数。
具体一点,我们知道endpos,可以找到对应parent tree的节点。
然后目标节点肯定是在根到此节点的路径上的(他是她的后缀嘛)。
用子串长度和节点的longest比较就行了,倍增慢慢跳。
总的复杂度\(O(nlogn)\)
当然还有更简单更优秀的的回文自动机。

卡常!!

脸丑的bzoj,luogu会MLE。
所以利用你们的卡常技巧
parent树的树高不会太高,数据中应该深度都小于1000,倍增开10倍。(有点面向数据了)
sam状态是3*n,用map或unordered_map牺牲一点复杂度去省空间(测得用map毫无用处,内存增大)。
当然,可以不用建立出parent tree的图,因为你已经有了父节点表示法的图了。
老方法:基数排序endpos去更新parent tree的size
还是wxy super cool.我再也不建parent树了,vector内存太大了。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=600007;
int read() {
    int x=0,f=1;char s=getchar();
    for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;
}
int n,len,p[N],st[N][11],c[N],a[N];
char s[N],S[N];
struct node {
    int len,fa,ch[26];
}dian[N];
int siz[N],tot=1,las=1,i_can_find_it[N];
void add(int c,int k_th) {
    int p=las;int np=las=++tot;
    dian[np].len=dian[p].len+1;
    for(;p&&!dian[p].ch[c];p=dian[p].fa) dian[p].ch[c]=np;
    if(!p) dian[np].fa=1;
    else {
        int q=dian[p].ch[c];
        if(dian[q].len==dian[p].len+1) dian[np].fa=q;
        else {
            int nq=++tot;dian[nq]=dian[q];
            dian[nq].len=dian[p].len+1;
            dian[q].fa=dian[np].fa=nq;
            for(;p&&dian[p].ch[c]==q;p=dian[p].fa)
                dian[p].ch[c]=nq;
        }
    }
    siz[las]=1;
    i_can_find_it[k_th]=las;
}
int query(int l,int r) {
    int u=i_can_find_it[r];
    for(int i=10;i>=0;--i)
        if(dian[st[u][i]].len>=r-l+1) u=st[u][i];
    return siz[u];
}
int main() {
// 	freopen("testdata.in","r",stdin);
    scanf("%s",s+1);
    n=strlen(s+1);
    //build sam
    for(int i=1;i<=n;++i) add(s[i]-'a',i);
    //build parent tree
    for(int i=2;i<=tot;++i) st[i][0]=dian[i].fa;
    for(int i=1;i<=tot;++i) c[dian[i].len]++;
    for(int i=1;i<=tot;++i) c[i]+=c[i-1];
    for(int i=1;i<=tot;++i) a[c[dian[i].len]--]=i;
    for(int i=tot;i>=1;--i) siz[dian[a[i]].fa]+=siz[a[i]];
    //init st
    for(int j=1;j<=10;++j)
        for(int i=1;i<=n;++i)
            st[i][j]=st[st[i][j-1]][j-1];
    //manacher -- init 
    S[0]='@';
    for(int i=1;i<=n+n;i+=2) S[i]='#',S[i+1]=s[i/2+1];
    S[len=n+n+1]='#';
    int id=0,mx=0;
    ll ans=0;
    //manacher -- do
    for(int i=1;i<=len;++i) {
        if(i<mx) p[i]=min(mx-i,p[id*2-i]);
        else p[i]=1;
        while(S[i+p[i]]==S[i-p[i]]) {
            int l=(i-p[i]+1)/2,r=(i+p[i])/2;
            if(l<=r) ans=max(ans,1LL*(r-l+1)*query(l,r));
            p[i]++;
        }
        if(mx < i+p[i]-1) mx=i+p[i]-1,id=i;
    }
    //print
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-07-13 09:51  复杂的哈皮狗  阅读(125)  评论(0编辑  收藏  举报