后缀自动机 - str2int - UVA - 1673
题意:
输 入 n 个 由 数 字 构 成 的 字 符 串 s 1 , s 2 , . . . , s n , 对 所 有 字 符 串 的 所 有 不 重 复 的 子 串 所 表 示 的 有 效 ( 不 含 前 导 0 ) 数 字 进 行 求 和 , 将 结 果 对 2012 取 模 。 输入n个由数字构成的字符串s_1,s_2,...,s_n,对所有字符串的所有不重复的子串所表示的有效(不含前导0)数字进行求和,\\将结果对2012取模。 输入n个由数字构成的字符串s1,s2,...,sn,对所有字符串的所有不重复的子串所表示的有效(不含前导0)数字进行求和,将结果对2012取模。
Sample Input:
5
101
123
09
000
1234567890
Sample Output:
202
题解:
为 了 避 免 对 n 个 数 字 分 别 建 立 后 缀 自 动 机 , 可 以 将 n 个 字 符 串 成 一 整 个 字 符 串 : 在 每 个 数 字 中 间 添 加 一 个 特 殊 分 隔 符 , 然 后 再 对 整 个 字 符 串 建 立 后 缀 自 动 机 。 这 样 , 在 树 上 转 移 的 时 候 , 不 向 分 隔 符 的 方 向 走 , 并 且 不 在 初 始 状 态 往 0 走 来 避 免 前 导 0 , 对 每 个 子 串 哈 希 再 求 和 即 可 。 为了避免对n个数字分别建立后缀自动机,可以将n个字符串成一整个字符串:\\在每个数字中间添加一个特殊分隔符,然后再对整个字符串建立后缀自动机。\\ \ \\这样,在树上转移的时候,不向分隔符的方向走,并且不在初始状态往0走来避免前导0,对每个子串哈希再求和即可。 为了避免对n个数字分别建立后缀自动机,可以将n个字符串成一整个字符串:在每个数字中间添加一个特殊分隔符,然后再对整个字符串建立后缀自动机。 这样,在树上转移的时候,不向分隔符的方向走,并且不在初始状态往0走来避免前导0,对每个子串哈希再求和即可。
具体落实:
① 、 将 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]:从SAM的初始状态到状态v中的所有不含分隔符以及前导0的数量。sum[v]:从初始状态到状态v的所有合法路径形成的数字之和。 则cnt[v]=∑cnt[u],其中状态v是由状态u转移来的,sum[v]=∑sum[u]×10+cnt[u]×i,其中cnt[u]是以状态u转移到状态v的子串数量,i是状态v的结尾数字。 以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;
}