• 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
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    [转载]SETSOCKOPT IOCTLSOCKET 设置非阻塞SOCKET函数
    [转载]FTP协议详解
    复制控制
    高手的C++学习忠告,虚心学习下~~[转载]
    [转载]Linux中硬链接和软链接的区别和联系
    以太网最小帧长度为什么要64个字节
    [转载]HTTP协议详解
    Netstat命令详解

    将1:N关系的界面上"添加现有****"按钮隐藏掉
  • 原文地址:https://www.cnblogs.com/dsrdsr/p/11179527.html
Copyright © 2020-2023  润新知