后缀自动机 - str2int - UVA - 1673

后缀自动机 - str2int - UVA - 1673

题意:

输 入 n 个 由 数 字 构 成 的 字 符 串 s 1 , s 2 , . . . , s n , 对 所 有 字 符 串 的 所 有 不 重 复 的 子 串 所 表 示 的 有 效 ( 不 含 前 导 0 ) 数 字 进 行 求 和 , 将 结 果 对 2012 取 模 。 输入n个由数字构成的字符串s_1,s_2,...,s_n,对所有字符串的所有不重复的子串所表示的有效(不含前导0)数字进行求和,\\将结果对2012取模。 ns1,s2,...,sn(0)2012

Sample Input:
5
101
123
09
000
1234567890

Sample Output:
202

题解:

为 了 避 免 对 n 个 数 字 分 别 建 立 后 缀 自 动 机 , 可 以 将 n 个 字 符 串 成 一 整 个 字 符 串 : 在 每 个 数 字 中 间 添 加 一 个 特 殊 分 隔 符 , 然 后 再 对 整 个 字 符 串 建 立 后 缀 自 动 机 。   这 样 , 在 树 上 转 移 的 时 候 , 不 向 分 隔 符 的 方 向 走 , 并 且 不 在 初 始 状 态 往 0 走 来 避 免 前 导 0 , 对 每 个 子 串 哈 希 再 求 和 即 可 。 为了避免对n个数字分别建立后缀自动机,可以将n个字符串成一整个字符串:\\在每个数字中间添加一个特殊分隔符,然后再对整个字符串建立后缀自动机。\\ \ \\这样,在树上转移的时候,不向分隔符的方向走,并且不在初始状态往0走来避免前导0,对每个子串哈希再求和即可。 nn: 00

具体落实:

① 、 将 n 个 串 合 并 预 处 理 , 并 建 立 后 缀 自 动 机 。   ② 、 设 c n t [ v ] : 从 S A M 的 初 始 状 态 到 状 态 v 中 的 所 有 不 含 分 隔 符 以 及 前 导 0 的 数 量 。 s u m [ v ] : 从 初 始 状 态 到 状 态 v 的 所 有 合 法 路 径 形 成 的 数 字 之 和 。   则 c n t [ v ] = ∑ c n t [ u ] , 其 中 状 态 v 是 由 状 态 u 转 移 来 的 , s u m [ v ] = ∑ s u m [ u ] × 10 + c n t [ u ] × i , 其 中 c n t [ u ] 是 以 状 态 u 转 移 到 状 态 v 的 子 串 数 量 , i 是 状 态 v 的 结 尾 数 字 。   以 123423 为 例 : 转 移 关 系 如 下 图 : ①、将n个串合并预处理,并建立后缀自动机。\\\ \\②、设cnt[v]:从SAM的初始状态到状态v中的所有不含分隔符以及前导0的数量。 \\\qquad sum[v]:从初始状态到状态v的所有合法路径形成的数字之和。\\\ \\则cnt[v]=\sum cnt[u],其中状态v是由状态u转移来的,\\\quad sum[v]=\sum sum[u]×10+cnt[u]×i,其中cnt[u]是以状态u转移到状态v的子串数量,i是状态v的结尾数字。\\\ \\ 以123423为例:转移关系如下图: n cnt[v]:SAMv0sum[v]:v cnt[v]=cnt[u]vusum[v]=sum[u]×10+cnt[u]×icnt[u]uviv 123423:

在这里插入图片描述


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>

using namespace std;
const int mod = 2012;
const int maxn = 2e5+1e2;
const int maxnode = maxn;
const int sigmaSize = 10;

char s[maxn];

int id(char c) { return c-'0'; }

struct SAM
{
    int n , last;
    int ch[maxnode][sigmaSize] , fa[maxnode] , mx[maxnode];
    int c[maxn] , q[maxnode];
    int sum[maxn] , cnt[maxn];

    int newNode()       { mx[++n] = 0; memset(ch[n], 0, sizeof(ch[n])); return n; }
    void init(){ mx[n = last = 1] = 0; memset(ch[1], 0, sizeof(ch[1]));           }

    void extend(int x)
    {
        int p = last , np = newNode();
        mx[last = np] = mx[p] + 1;

        while(p && !ch[p][x]) ch[p][x] = np , p = fa[p];

        if(!p) fa[np] = 1;
        else
        {
            int q = ch[p][x];
            if(mx[q] == mx[p]+1) fa[np] = q;
            else
            {
                int nq = newNode();
                mx[nq] = mx[p] + 1;
                memcpy(ch[nq], ch[q], sizeof(ch[0]));
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;

                while(p && ch[p][x] == q) ch[p][x] = nq , p = fa[p];
            }
        }
    }

    void Sort()
    {
        memset(c, 0, sizeof(int)*(n+1));
        for(int i=1;i<=n;i++) c[mx[i]]++;
        for(int i=1;i<=n;i++) c[i] += c[i-1];
        for(int i=1;i<=n;i++) q[c[mx[i]]--] = i;
    }

    int dp()
    {
        Sort();
        memset(sum, 0, sizeof(int)*(n+1));
        memset(cnt, 0, sizeof(int)*(n+1));

        sum[1] = 0; cnt[1] = 1;
        for(int i=1 , j;i<=n;i++)
        {
            j = q[i];
            for(int k=0,t;k<sigmaSize;k++)
            {
                if(j==1 && !k) continue;
                t = ch[j][k];
                (sum[t] += cnt[j]*k + sum[j]*10) %= mod;
                (cnt[t] += cnt[j])               %= mod;
            }
        }
        int res = 0;
        for(int i=1;i<=n;i++) (res += sum[i]) %= mod;
        return res;
    }
}solver;

int n;

int main()
{
    while(cin>>n)
    {
        solver.init();
        while(n--)
        {
            scanf("%s" , s);
            solver.last = 1;
            for(int i=0,j=strlen(s);i<j;i++) solver.extend(id(s[i]));
        }

        cout<<solver.dp()<<endl;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值