题目大意:
对于给出的n个字符串(n <= 1e5), 每个字符串包含字符'0' ~ '9', 对于每个字符串的子串, 对应一个整数值, 求所有n个字符串的所有子串转换成整数后出现的不同的整数的和对 2012 取模的值, 每组测试数据的所有字符串的总长度不超过1e6
例如串"101"子串有 1, 10, 101, 0, 01, 1, 对应的不同整数是1, 10, 101 (0对求和没有影响, 可以略去)
大致思路:
首先第一眼看得出和后缀数组有关, 但是将所有的串连接起来之后, 有一些计数上的细节需要考虑, 首先如果后缀以'0'开头则不需要加入计数(这样的后缀的所有子串对应的整数一定会在其他的后缀当中出现, 或者子串对应的值为0, 没有影响), 对于一个后缀数组sa, 从第一个到最后一个, 如果肯定需要计数的是 sa[i] + height[i] ~ i所在字符串的结尾位置,那么这样的字符串如何计数呢? 很明显如果对于每一个后缀从sa[i]开始的话, 依次计算sa[i], sa[i]sa[i] + 1...对应的子串转换成整数的值的话肯定会超时, 所以需要进行预处理
定义前缀和sigma[i] = s0 + s0s1 + s0s1s2 + ... + (s0s1s2s2s4...si)这一连续的整数值定义rest[i] = s0s1s2s3s4...si
也就是说sigma[i]是rest[i]的前缀和
例如对于字符串 s = “12345” sigma[0] = 1, sigma[1] = 1 + 12, sigma[3] = 1 + 12 + 123...
rest[0] = 1, rest[1] = 12, rest[3] = 123...
对于用未出现字符隔开的连起来的总串, 预处理出sigma, rest数组, 用tens[i] 表示(∑10^j) % 2012 (1 <= j <= i)
就和容易找到连续的起点在sa[i], 终点在sa[i] + height[i] ~i所在字符串的整数值的和了
例如对于“12345” 查询3 + 34 + 345那么 3 + 34 + 345 = (123 + 1234 + 12345) - 12*(10 + 100 + 1000) = (sigma[4] - sigma[1] + 2012 - rest[1]*tens[3]) % 2012
想到这个前缀和的关系剩下的就很好做了
另外还有后缀自动机的做法:
将所有串中间中10隔开连接起来建立后缀自动机, 然后从根节点开始按照拓扑序向下遍历, 计算出到达每一个结点的方案数(不能沿着10走, 根节点还不能沿着0走), 然后对于状态 s 经过边 j 到达 t 状态, t 状态中从s转移来的字符串的贡献是 s状态中字符串的贡献*10 + j*s中不同满足条件的字符串数量, 拓扑序遍历即可
代码如下:
后缀数组解法:
Result : Accepted Memory : 17392 KB Time : 187 ms
/*
* Author: Gatevin
* Created Time: 2015/3/9 15:33:02
* File Name: Kotori_Itsuka.cpp
*/
#include<cstdio>
#include<cstring>//之前写了很多头文件导致rank数组模糊定义了..看来HDU上交后缀数组还是要注意一下
using namespace std;
const double eps(1e-8);
typedef long long lint;
const int mod = 2012;
#define maxn 1000010
int wa[maxn], wb[maxn], wv[maxn], Ws[maxn];
int cmp(int *r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *r, int *sa, int n, int m)
{
int *x = wa, *y = wb, *t, i, j, p;
for(i = 0; i < m; i++) Ws[i] = 0;
for(i = 0; i < n; i++) Ws[x[i] = r[i]]++;
for(i = 1; i < m; i++) Ws[i] += Ws[i - 1];
for(i = n - 1; i >= 0; i--) sa[--Ws[x[i]]] = i;
for(j = 1, p = 1; p < n; j *= 2, m = p)
{
for(p = 0, i = n - j; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
for(i = 0; i < n; i++) wv[i] = x[y[i]];
for(i = 0; i < m; i++) Ws[i] = 0;
for(i = 0; i < n; i++) Ws[wv[i]]++;
for(i = 1; i < m; i++) Ws[i] += Ws[i - 1];
for(i = n - 1; i >= 0; i--) sa[--Ws[wv[i]]] = y[i];
for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
}
return;
}
int rank[maxn], height[maxn];
void calheight(int *r, int *sa, int n)
{
int i, j, k = 0;
for(i = 1; i <= n; i++) rank[sa[i]] = i;
for(i = 0; i < n; height[rank[i++]] = k)
for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++);
return;
}
int n, N;
char in[maxn];
int s[maxn], sa[maxn];
int end[maxn];
int sigma[maxn];
int rest[maxn];
int tens[maxn];//tens[i] = (∑10^j) % mod (1 <= j <= i)
int main()
{
while(scanf("%d", &n) != EOF)
{
N = 0;
for(int i = 0; i < n; i++)
{
scanf("%s", in);
int tmp = strlen(in);
int pos = N + tmp - 1;
for(int j = 0; j < tmp; j++)
{
end[N] = pos;
s[N++] = in[j] - '0' + 1;
}
end[N] = pos;
s[N++] = 11;
}
N--;
s[N] = 0;
da(s, sa, N + 1, 12);
calheight(s, sa, N);
memset(sigma, 0, sizeof(sigma));
memset(rest, 0, sizeof(rest));
memset(tens, 0, sizeof(tens));
rest[0] = (s[0] - 1) % mod;
sigma[0] = rest[0];
tens[0] = 0;
int ten = 1;
for(int i = 1; i <= N; i++)
{
rest[i] = (rest[i - 1] * 10 + s[i] - 1) % mod;
sigma[i] = (sigma[i - 1] + rest[i]) % mod;
ten = ten*10 % mod;
tens[i] = (tens[i - 1] + ten) % mod;
}
int ans = 0;
for(int i = 1; i <= N; i++)
{
if(s[sa[i]] == 1) continue;
int start = sa[i] + height[i];
int tail = end[sa[i]];
if(tail < start) continue;
if(start == 0)
ans = (ans + sigma[tail]) % mod;
else
ans = (ans + sigma[tail] - sigma[start - 1] + mod - rest[sa[i] - 1] * (tens[tail - sa[i] + 1] - tens[start - sa[i]] + mod) % mod + mod) % mod;
}
printf("%d\n", ans);
}
return 0;
}
后缀自动机的做法:
Result : Accepted Memory : 16732 KB Time : 249 ms
/*
* Author: Gatevin
* Created Time: 2015/4/16 13:04:40
* File Name: Rin_Tohsaka.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl
#define maxn 222000
#define maxm 111000
const int mod = 2012;
struct Suffix_Automation
{
struct State
{
State *par;
State *go[12];
int right, val, mi, sum, cnt;
void init(int _val)
{
par = 0, val = _val, right = mi = sum = cnt = 0;
memset(go, 0, sizeof(go));
}
};
State *root, *last, *cur;
State nodePool[maxn];
State* newState(int val = 0)
{
cur->init(val);
return cur++;
}
void initSAM()
{
cur = nodePool;
root = newState();
last = root;
}
void extend(int w, int len)
{
State *p = last;
State *np = newState(p->val + 1);
np->right = 1;
while(p && p->go[w] == 0)
{
p->go[w] = np;
p = p->par;
}
if(p == 0)
{
np->par = root;
}
else
{
State *q = p->go[w];
if(q->val == p->val + 1)
{
np->par = q;
}
else
{
State *nq = newState(p->val + 1);
memcpy(nq->go, q->go, sizeof(q->go));
nq->par = q->par;
q->par = nq;
np->par = nq;
while(p && p->go[w] == q)
{
p->go[w] = nq;
p = p->par;
}
}
}
last = np;
}
int d[maxm];
State *b[maxn];
void topo()
{
int cnt = cur - nodePool;
int maxVal = 0;
memset(d, 0, sizeof(d));
for(int i = 1; i < cnt; i++)
maxVal = max(maxVal, nodePool[i].val), d[nodePool[i].val]++;
for(int i = 1; i <= maxVal; i++) d[i] += d[i - 1];
for(int i = 1; i < cnt; i++) b[d[nodePool[i].val]--] = &nodePool[i];
b[0] = root;
}
/*
void SAMInfo()
{
int cnt = cur - nodePool;
State *p;
for(int i = cnt - 1; i > 0; i--)
{
p = b[i];
p->par->right += p->right;
p->mi = p->par->val + 1;
}
}
*/
};
Suffix_Automation sam;
char s[maxn];
int pre[maxn];
int main()
{
int n;
while(~scanf("%d", &n))
{
sam.initSAM();
while(n--)
{
scanf("%s", s);
int len = strlen(s);
for(int i = 0; i < len; i++)
sam.extend(s[i] - '0', i + 1);
sam.extend(10, -1);
}
sam.topo();
//sam.SAMInfo();
int ans = 0;
int cnt = sam.cur - sam.nodePool;
sam.b[0]->cnt = 1;
for(int i = 0; i < cnt; i++)
{
for(int j = 0; j < 10; j++)//不沿着10走
{
if(!i && !j) continue;
if(!sam.b[i]->go[j]) continue;
sam.b[i]->go[j]->sum = (sam.b[i]->go[j]->sum + sam.b[i]->sum*10 + sam.b[i]->cnt*j) % mod;
sam.b[i]->go[j]->cnt = (sam.b[i]->go[j]->cnt + sam.b[i]->cnt) % mod;
}
ans = (ans + sam.b[i]->sum) % mod;
}
printf("%d\n", ans);
}
return 0;
}