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;
}
全部评论

相关推荐

06-25 16:00
武汉大学 Java
工科研究生底薪工资就开3k啊??
机械打工仔:写文章提成的岗位工资低,你怪工科?
点赞 评论 收藏
分享
求offer的大角牛:不吃香菜
点赞 评论 收藏
分享
06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务