hihoCoder1457—后缀自动机四·重复旋律7

题目链接

时间限制: 15000ms
单点时限: 3000ms
内存限制: 512MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。

神奇的是小Hi发现了一部名字叫《十进制进行曲大全》的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字。

现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0)。答案有可能很大,我们需要对(10^9 + 7)取摸。

解题方法提示

输入

第一行,一个整数N,表示有N部作品。

接下来N行,每行包含一个由数字0-9构成的字符串S。

所有字符串长度和不超过 1000000。

输出

共一行,一个整数,表示答案 mod (10^9 + 7)。

样例输入
2
101
09
样例输出
131


解题思路:

有点类似于求子串的个数,所以一开始想的是记忆化搜索,类似于dp[v] = sum(dp[u]*10+'c'-'0'),但因为包含多个串(这里在每个子串的末尾添加一个字符‘A’,然后将这些字符串合成一个字符串),向上回朔时碰到‘A’的话,此时dp[u]*10就会发生错误,导致结果远大于答案。

这里对每个节点状态的子串和进行计算,用sum[v]表示。首先对单个字符分析,若v能通过字符c转移到状态u,此时sum[u] = sum[v]*10+subnum(u)*(c-'0'),其中subnum(u)为状态u的子串个数。现在我们考虑多个字符串的情况。对于状态v的子串中包含‘A’的我们应不予考虑,所以sum[v] = sum[u]*10+noA_subnum(u)*(c-'0'),noA_subnum(u)表示不包括‘A’的子串个数。即noA_subnum(u)表示从初始状态到状态u不包含‘A’的路径条数,这里我们可以用拓扑排序来处理。


代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <queue>

using namespace std;

typedef long long llt;

const int N = 1000010;
const int mod = 1e9+7;

struct state{
    int len,link;
    map<char,int>next;
}st[N*2];

int sz,last,indeg[N*2],cnt[N*2];
llt sum[N*2];

void sa_init()
{
    sz = last = 0;
    st[0].len = 0;
    st[0].link = -1;
    ++sz;

    for(int i = 0; i < N; ++i)
        st[i].next.clear();

    memset(indeg,0,sizeof(indeg));
    memset(cnt,0,sizeof(cnt));
}

void sa_extend(char c)
{
    int cur = sz++;         //新添加的状态结点
    st[cur].len = st[last].len+1;
    int p;
    for(p = last; p != -1 && !st[p].next.count(c); p = st[p].link)
        st[p].next[c] = cur,indeg[cur]++;
    if(p == -1)
        st[cur].link = 0;
    else{
        int q = st[p].next[c];
        if(st[p].len+1 == st[q].len)
            st[cur].link = q;
        else{
            int clone = sz++;
            st[clone].len = st[p].len+1;
            st[clone].next = st[q].next;

            for(auto it = st[q].next.begin(); it != st[q].next.end(); ++it){
                int t = (*it).second; indeg[t]++;
            }

            st[clone].link = st[q].link;

            for(; p != -1 && st[p].next[c] == q; p = st[p].link){
                int t = st[p].next[c];
                st[p].next[c] = clone;
                indeg[t]--; indeg[clone]++;
            }

            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}

void bfs()
{
    queue<int>q;
    for(int i = 0; i < sz; ++i) if(indeg[i] == 0) q.push(i);
    cnt[0] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(auto it = st[u].next.begin(); it != st[u].next.end(); ++it){
            int v = (*it).second;
            char ch = (*it).first;
            indeg[v]--;
            if(indeg[v] == 0) q.push(v);
            if(ch == 'A') continue;
            cnt[v] += cnt[u];
            sum[v] = (sum[v]+sum[u]*10+cnt[u]*(ch-'0'))%mod;
        }
    }
}

int main()
{
    sa_init();
    string str,s; int n;
    cin >> n;
    for(int i = 0; i < n; ++i) cin >> s,str += s+'A';
    for(int i = 0; i < str.length(); ++i)
        sa_extend(str[i]);

    bfs();
    llt ans = 0;
    for(int i = 0; i < sz; ++i)
        ans = (ans+sum[i])%mod;

    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jiangzhiyuan123/article/details/79919782