• hiho#1457 重复旋律7 求子串和 后缀自动机


    题目传送门

    题意:

      给出若干个串,求所有子串的和,子串和的定义为十进制数,取模1e9+7.

    思路:

      对于一个串来说,一个状态p就代表着$right$相同的集合,假设我们已经知道了状态p的$sum$,以及状态p的$size$,假设p的下一位有一个c,p+c的状态为q,那么$sum[q]+=sum[p]*10+c*size[p]$,并且要更新$size[q]$,注意这里是“+=”,因为q也有可能通过其他方式得到。

      而这道题的终点就是如何转移,显然是用拓扑,但困扰了我好久的就是如何处理一开始每个点的入度。

      这是我一开始的代码。

      

        for(int i=1;i<=tot;i++){
            for(int j=0;j<10;j++){
                in[ch[i][j]]++;
            }
        }

      这个代码的意思就是我把每个点和后面能抵达的点全部建边了,但这样答案会少,原因是有些点的入度永远不会0,所以少计算了,这个入度处理方式是错误的。

      为什么呢?因为我们在处理多个串的时候,需要用一个':'符号来连接两个字符串,而这个符号刚好比‘9’大1,而我们下面用拓扑进行dp的时候,由于我们根本不会在队列里放入":"这个字符,所以形如$:5$这样的入度永远不会被减去,就无法入队。

      所以我们处理入度的方式还是要用拓扑。

    #include<bits/stdc++.h>
    #define clr(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const ll mod=1e9+7;
    const int maxn=1000100;
    char s[maxn];
    int len[maxn<<1],ch[maxn<<1][11],fa[maxn<<1],tot=1,root=1,last=1,siz,r[maxn<<1];
    int a[maxn<<1],c[maxn<<1],ans[maxn<<1];
    ll dp[maxn<<1];
    void extend(int x){
        int now=++tot,pre=last;
        last=now,len[now]=len[pre]+1;
        while( pre && !ch[pre][x]){
            ch[pre][x]=now;
            pre=fa[pre];
        }
        if(!pre)fa[now]=root;
        else{
            int q = ch[pre][x];
            if(len[q]==len[pre]+1)fa[now]=q;
            else {
                int nows=++tot;
                memcpy(ch[nows],ch[q],sizeof(ch[q]));
                len[nows]=len[pre]+1;
                fa[nows]=fa[q];
                fa[q]=fa[now]=nows;
                while(pre&&ch[pre][x]==q){
                    ch[pre][x]=nows;
                    pre=fa[pre];
                }
            }
        }
    }
    bool vis[maxn<<1];
    int in[maxn<<1];
    int main(){
        int n;
        cin>>n;
        while(n--){
            scanf("%s",s);
            siz=strlen(s);
            for(int i=0;i<siz;i++)
            {
                int p=s[i]-'0';
                extend(p);
            }
            extend(10);
        }
        queue<int >q;
        q.push(1); vis[1] = 1;
        while(!q.empty()){
            int x = q.front(); q.pop();
            for(int c = 0; c < 10; c++){
                int y = ch[x][c];
                if(y == 0) continue;
                if(!vis[y]) 
                q.push(y); vis[y] = 1;
                in[y]++;
            }
        }
        q.push(1);
        r[1]=1;
        while(!q.empty()){
            int i=q.front();
            q.pop();
            for(int j=0;j<10;j++){
                int p=ch[i][j];
                if(p){
                    r[p]=(r[p]+r[i])%mod;
                    dp[p]=(dp[p]+dp[i]*10%mod+j*r[i]%mod)%mod;
                    if(--in[p]==0){
                        q.push(p);
                    }
                }
            }
        }
    
        ll res=0;
        for(int i=1;i<=tot;i++){
            res=(res+dp[i])%mod;
        }
        cout<<res<<endl;
    
    }

      

  • 相关阅读:
    二叉树的创建、递归,非递归遍历
    一文读懂PID控制算法(抛弃公式,从原理上真正理解PID控制)
    说走就走的旅行 ——一个人重游长安
    OpenCV笔记:pyrDown()函数和pryUp()函数的使用
    考研心得--一个差劲的ACMer
    HDU 3530 --- Subsequence 单调队列
    POJ 3494 Largest Submatrix of All 1’s 单调队列||单调栈
    极角排序详解:
    POJ 3268 Silver Cow Party 最短路—dijkstra算法的优化。
    给出 中序&后序 序列 建树;给出 先序&中序 序列 建树
  • 原文地址:https://www.cnblogs.com/mountaink/p/10665905.html
Copyright © 2020-2023  润新知