zoukankan      html  css  js  c++  java
  • 后缀自动机 ( SAM ) 模板

    参考博客 I参考博客 II

    学习参考 hihocoder

    struct SAM
    {
        static const int MAXN = ((int)1e6 + 10)<<1;///大小为字符串长度两倍
        static const int LetterSize = 26;///字符集大小
    
        int tot;/// 总节点个数、从 1 开始、且 1 代表空串
        int last;///构造 SAM 时候、每次添加新字母时所处的状态
        int ch[MAXN][LetterSize];///每个状态对应字母的转移指针、类似字典树
        int fa[MAXN];///每个节点所处的 Suffix-path 上的父亲节点
        int len[MAXN];///每个节点最长的字符的长度、对应最短的应该是 len[fa[i]]、故字符个数个数为 len[i] - len[fa[i]]
        int tpSum[MAXN];///拓扑排序辅助数组
        int tp[MAXN];///保存拓扑排序后的结果
        int cnt[MAXN];///每个节点所有的字符串在整串中出现的次数
    
        void init(void){
            last = tot = 1;
            len[1] = 0;
            memset( ch[1], 0, sizeof ch[1]);
            memset( cnt , 0, sizeof cnt);
        }
    
        void add( int x){
            int p = last, np = last = ++tot;
            len[np] = len[p] + 1, cnt[last] = 1;
            memset( ch[np], 0, sizeof ch[np]);
            while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
            if( p == 0)
                fa[np] = 1;
            else
            {
                int q = ch[p][x];
                if( len[q] == len[p] + 1)
                    fa[np] = q;
                else
                {
                    int nq = ++tot;
                    memcpy( ch[nq], ch[q], sizeof ch[q]);
                    len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
                    while( p && ch[p][x] == q)  ch[p][x] = nq, p = fa[p];
                }
            }
        }
    
        void toposort(void){
            for(int i = 1; i <= len[last]; i++)   tpSum[i] = 0;
            for(int i = 1; i <= tot; i++)   tpSum[len[i]]++;
            for(int i = 1; i <= len[last]; i++)   tpSum[i] += tpSum[i-1];
            for(int i = 1; i <= tot; i++)   tp[tpSum[len[i]]--] = i;
            for(int i = tot; i; i--)   cnt[fa[tp[i]]] += cnt[tp[i]];
    
    //        for(int i=1; i<=tot; i++) printf("%d ", tp[i]); puts("");
    //        for(int i=1; i<=tot; i++) printf("%d ", cnt[tp[i]]); puts("");
    //        for(int i=1; i<=tot; i++) printf("%d ", len[tp[i]]); puts("");
        }
    
        LL GetAns(){
            ///...
    //        LL ret = 0;
    //        for(int i=2; i<=tot; i++)
    //            ret += 1LL * len[i] - 1LL * len[fa[i]];
    //        return ret;
        }
    
    } sam;
    Vision I
    const int MAXL=1000005;
    const int MAXS=26;
    
    struct SAM
    {
        int n=0, len, st;
        //maxlen表示每个状态对应的子串中长度最大值,minlen最小值
        //trans是转移边,根节点是1,所以0代表不存在
        //slink表示绿色的suffix link
        //col表示节点颜色,col=1代表是后缀节点,=0代表不是后缀的节点
        //indeg表示对于只含有suffix link的图的度,统计endposamu用
        //endposa表示该状态对应的子串们出现的结尾位置,amu表示endpos集合的大小
        //ans表示某一长度的子串的个数
        //sam中每个状态字符串长度是连续的,要求某个状态对应的子串个数只需maxlen-minlen+1即可
        int maxlen[2*MAXL+10], minlen[2*MAXL+10], trans[2*MAXL+10][MAXS], slink[2*MAXL+10], col[2*MAXL+10], indeg[2*MAXL+10], endposamu[2*MAXL+10], ans[MAXL];
    
        int new_state(int _maxlen, int _minlen, int* _trans, int _slink)
        {
            n++;
            maxlen[n]=_maxlen;
            minlen[n]=_minlen;
            for(int i=0; i<MAXS; i++)
            {
                if(_trans==NULL)
                    trans[n][i]=0;
                else
                    trans[n][i]=_trans[i];
            }
            slink[n]=_slink;
            return n;
        }
    
        int add_char(char ch, int u)
        {
            int c=ch-'a';
            int z=new_state(maxlen[u]+1, -1, NULL, 0);
            col[z]=1;
            int v=u;
            while(v!=0&&trans[v][c]==0)
            {
                trans[v][c]=z;
                v=slink[v];
            }
            if(v==0)//最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移
            {
                minlen[z]=1;
                slink[z]=1;
                indeg[1]++;
                return z;
            }
            int x=trans[v][c];
            if(maxlen[v]+1==maxlen[x])//较简单的情况,不用拆分x
            {
                minlen[z]=maxlen[x]+1;
                slink[z]=x;
                indeg[x]++;
                return z;
            }
            int y=new_state(maxlen[v]+1, -1, trans[x], slink[x]); //最复杂的情况,拆分x
            col[y]=0;
    
            minlen[x]=maxlen[y]+1;
            slink[x]=y;
            minlen[z]=maxlen[y]+1;
            slink[z]=y;
            indeg[y]+=2;
            int w=v;
            while(w!=0&&trans[w][c]==x)
            {
                trans[w][c]=y;
                w=slink[w];
            }
            minlen[y]=maxlen[slink[y]]+1;
            return z;
        }
        void init()
        {
            memset(col, 0, sizeof(col));
            memset(indeg, 0, sizeof(indeg));
            memset(maxlen, 0, sizeof(maxlen));
            memset(minlen, 0, sizeof(maxlen));
            memset(trans, 0, sizeof(maxlen));
            memset(slink, 0, sizeof(maxlen));
            memset(endposamu, 0, sizeof(endposamu));
            n=0;//多case也要清空n
            st=new_state(0, -1, NULL, 0);
        }
        void getendpos()
        {
            queue<int> que;
            for(int i=st;i<=n;i++)
            {
                if(indeg[i]==0) que.push(i);
                if(col[i]==1) endposamu[i]++;
            }
            while(!que.empty())
            {
                int pos=que.front();que.pop();
                endposamu[slink[pos]]+=endposamu[pos];
                indeg[slink[pos]]--;
                if(indeg[slink[pos]]==0) que.push(slink[pos]);
            }
        }
        void addstring(string s)
        {
            int la=st;
            for(auto tmp:s)
            {
                la=add_char(tmp, la);
            }
            getendpos();
            for(int i=st;i<=n;i++)
            {
                ans[maxlen[i]]=max(ans[maxlen[i]], endposamu[i]);
            }
            for(int i=s.length()-1;i>=1;i--)
            {
                ans[i]=max(ans[i], ans[i+1]);
            }
        }
    }sam;
    Vision II
  • 相关阅读:
    在mysql数据库中制作千万级测试表
    Oracle生成千万测试数据
    mysql ODBC 在64位下提示找不到odbc驱动问题
    mysql指定某行或者某列的排序
    Python_Openpyxl 浅谈(最全总结 足够初次使用)
    Python3 多线程
    python爬虫项目-一见倾心壁纸
    gisoracle做windows界面
    Python 画 直方图/条形图/柱状图
    做动画animation--matplotlib--python2和3通用代码
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9683268.html
Copyright © 2011-2022 走看看