zoukankan      html  css  js  c++  java
  • 2019年华南理工大学程序设计竞赛(春季赛) K Parco_Love_String(后缀自动机)找两个串的相同字串有多少

    https://ac.nowcoder.com/acm/contest/625/K

    题意:

    给出Q 个询问 i , 求 s[0..i-1] 与 s[i...len-1] 有多少相同的字串

    分析:

    给出了查询 , 容易想到先预处理出答案好吧 , 字符串的问题也容易想到后缀自动机 ,但是我们该怎么使用呢?

    下面提供我的思路;

    我们建立出SAM后 , 跑一边拓扑排序 ,根据SAM跑出来的拓扑排序的序列特性 , 我们可以求出 在当前状态st 的最大串字符出现的个数

            for (int i = now; i >= 1; --i) {///得到的是最大字符串的出现次数
                int x = rank[i];
                endpos[slink[x]] += endpos[x];
            }

    可是这次我们需要求的是相同的串有多少 , 我们不可以暴力出SAM里面存有的串的个数 , 现在就来搞一个很奇妙的东西,

    我们可以根据上面求出来的endpos , 去推出 有多少相同的字符串;

            for(int i=1 ; i<=now; i++)///得到全部串的出现次数
            {
                int x=rank[i]; ///到x这个状态时 , 有多少的后缀总共串
                sum[x] = sum[slink[x]] + endpos[x]*(maxlen[x] - maxlen[slink[x]]);
                //cout<<sum[x]<<endl;
            }

    我们知道 对于 now1 , 与now2=slink[now1] , 如果now1状态出现了 , 那么now2 的状态也肯定会出现 , 因为silnk 是链接now1接下去的后缀 ,也就是说now2 是now1的后缀

    所以我们求当前now 有多少串相同的时候 , 就要+上一个的后缀价值 sum[x] = sum[slink[x]】 + 当前的价值

    当前的价值又是 怎么计算呢?

    我们知道 maxlen[x] - maxlen[slink[x]]  是表示当前的状态x 里面有多少的串 , 那这个状态出现的次数与包含的串相乘 , 不就是当前我们需要求的价值了吗

    上面可能说的比较乱 , 主要是我巨菜不知如何表达鸭

    上面是用str1 串去构建的SAM , 然后用str2 在这个自动机里面跑 , 与求LCA 很相似

    可以参考https://www.cnblogs.com/shuaihui520/p/10686862.html

    #include <bits/stdc++.h>
    #define LL long long
    #define P pair<int, int>
    #define lowbit(x) (x & -x)
    #define mem(a, b) memset(a, b, sizeof(a))
    #define rep(i, a, n) for (int i = a; i <= n; ++i)
    #define mid ((l + r) >> 1)
    #define lc rt<<1
    #define rc rt<<1|1
    #define ll long long
    using namespace std;
    const int maxn = 1005;
    struct SAM{
    
        int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1];
        // 用来求endpos
        int indegree[maxn<<1], endpos[maxn<<1], rank[maxn<<1], ans[maxn<<1];
        // 计算所有子串的和(0-9表示)
        LL sum[maxn<<1],D[maxn];
        int last, now, root;
    
        inline void newnode (int v) {
            maxlen[++now] = v;
            mem(trans[now],0);
        }
    
        inline void extend(int c) {
            newnode(maxlen[last] + 1);
            int p = last, np = now;
            // 更新trans
            while (p && !trans[p][c]) {
                trans[p][c] = np;
                p = slink[p];
            }
            if (!p) slink[np] = root;
            else {
                int q = trans[p][c];
                if (maxlen[p] + 1 != maxlen[q]) {
                    // 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q]
                    newnode(maxlen[p] + 1);
                    int nq = now;
                    memcpy(trans[nq], trans[q], sizeof(trans[q]));
                    slink[nq] = slink[q];
                    slink[q] = slink[np] = nq;
                    while (p && trans[p][c] == q) {
                        trans[p][c] = nq;
                        p = slink[p];
                    }
                }else slink[np] = q;
            }
            last = np;
            // 初始状态为可接受状态
            endpos[np] = 1;
        }
    
    
        inline void init()
        {
            root = last = now = 1;
            slink[root]=0;
            mem(trans[root],0);
            mem(endpos,0);
            mem(sum,0);
            mem(indegree,0);
            mem(rank,0);
        }
    
        inline void getEndpos() {
            // topsort
            for (int i = 1; i <= now; ++i) indegree[ maxlen[i] ]++; // 统计相同度数的节点的个数
            for (int i = 1; i <= now; ++i) indegree[i] += indegree[i-1];  // 统计度数小于等于 i 的节点的总数
            for (int i = 1; i <= now; ++i) rank[ indegree[ maxlen[i] ]-- ] = i;  // 为每个节点编号,节点度数越大编号越靠后
            // 从下往上按照slik更新
            for (int i = now; i >= 1; --i) {///得到的是最大字符串的出现次数
                int x = rank[i];
                endpos[slink[x]] += endpos[x];
            }
            for(int i=1 ; i<=now; i++)///得到全部串的出现次数
            {
                int x=rank[i]; ///到x这个状态时 , 有多少的后缀总共串
                sum[x] = sum[slink[x]] + endpos[x]*(maxlen[x] - maxlen[slink[x]]);
                //cout<<sum[x]<<endl;
            }
        }
        ///用一个串去跑的自动机
        inline void work(string s,int W)
        {
            getEndpos();
            int len=s.size();
            int now=root;
            int t1=0;
            ll ret=0;
            for(int i=0 ; i<len ; i++)
            {
                int nowid=s[i]-'a';
                if(trans[now][nowid])///这个状态有了 , 去下一个状态找
                {
                    t1++;
                    now=trans[now][nowid];
                    //ret+=sum[slink[now]] + endpos[now]*(t1-maxlen[slink[now]]);
                }
                else
                {   while(now!=0 && trans[now][nowid]==0) {now=slink[now];}///缩小范围找满足条件的
    
                    if(now)
                    {
                        t1 = maxlen[now]+1;
                        now=trans[now][nowid];
    
                    }
                    else
                    {
                        t1=0;now=root;
                    }
                }
                ret+=sum[slink[now]] + endpos[now]*(t1-maxlen[slink[now]]);
            }
            D[W]=ret;
            //return ret;
        }
    
    
    }sam;
    
    int main()
    {
    
    
        string T;cin>>T;
        int len=T.size();
    
        for(int i=1 ; i<len ; i++)
        {
            string t2;
            sam.init();
            for(int j=0 ; j<i ; j++)
            {
                sam.extend(T[j]-'a');
            }
    
            for(int j=i ; j<len ; j++)
            {
                t2+=T[j];
            }
            sam.work(t2,i);
    
        }
        int E;scanf("%d",&E);
        while(E--)
        {
            int x;
            scanf("%d",&x);
            printf("%lld
    ",sam.D[x]);
        }
    
    
       //- sam.all();
    }
    View Code
  • 相关阅读:
    Node Js
    动态遍历接收的参数
    负载均衡和springcloud.Ribbon
    Spring Cloud
    springboot整合hibernate
    python模块知识总结
    什么是netty--通俗易懂
    IDEA 的操作与使用
    spring 整合guava
    log4j向指定文件输出日志
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/10713336.html
Copyright © 2011-2022 走看看