洛谷传送门
BZOJ传送门
题目描述
阿米巴是小强的好朋友。
在小强眼中,阿米巴是一个作文成绩很高的文艺青年。为了获取考试作文的真谛,小强向阿米巴求教。阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范文拼拼凑凑而成的。小强不禁向阿米巴投去了疑惑的眼光,却发现阿米巴露出了一个狡黠的微笑。
为了有说服力地向阿米巴展示阿米巴的作文是多么让人觉得“眼熟”,小强想出了一个评定作文 “熟悉程度”的量化指标:L 0 .小强首先将作文转化成一个 01 串。之后,小强搜集了各路名家的文章,同样分别转化成 01 串后,整理出一个包含了 M M M 个 01 串的“ 标准作文库 ”。
小强认为:如果一个 01 串长度不少于 L L L 且在 标准作文库 中的某个串里出现过(即,它是 标准作文库 的 某个串 的一个 连续子串 ),那么它是“ 熟悉 ”的。对于一篇作文(一个 01 串) A A A,如果能够把 A A A 分割成若干段子串,其中“ 熟悉 ” 的子串的 长度 总 和 不少于 A 总 长度的 90 % 90\% 90%,那么称 A A A 是 “ 熟悉的文章 ”。 L 0 是 能够让 A A A 成为 “ 熟悉的文章 ” 的 所有 L L L 的最大值 (如果不存在这样的 L L L,那么规定 L 0 =0)。
举个例子:
小强的作文库里包含了如下 2 2 2 个字符串:
10110
000001110
有一篇待考察的作文是:
1011001100
小强计算出这篇作文 L L L 的最大值是 4 4 4,因为待考察的作文可以视作’10110’+‘0110’+‘0’,其中’10110’和’0110’被判定为 “ 熟悉 ” 的。而当 L = 5 L = 5 L=5 或是更大的时候,不存在符合题意的分割方法。所以,这篇作文的 L 0 = 4。小强认为阿米巴作文的 L 0 值比其他同学的明显要大。请你帮他验证一下。
输入输出格式
输入格式:
输入文件 cheat.in 第一行是两个整数 N , M N, M N,M,表示待检查的作文数量,和小强的标准作文库的行数。
接下来是 M M M 行的 01 串,表示标准作文库。
接下来是 N N N 行的 01 串,表示 N N N 篇作文。
输出格式:
输出文件 cheat.out 包含 N N N 行,每一行包含一个整数,表示该篇作文的 L 0 值。
输入输出样例
输入样例#1:
1 2
10110
000001110
1011001100
输出样例#1:
4
说明
对于 30 % 30\% 30%的测试数据,输入文件的长度不超过 1000 字节。
对于 50 % 50\% 50%的测试数据,输入文件的长度不超过 61000 字节。
对于 80 % 80\% 80%的测试数据,输入文件的长度不超过 250000 字节。
对于 100 % 100\% 100%的测试数据,输入文件的长度不超过 1100000 字节。
解题分析
首先还是很明显的二分答案, 转化为判定性问题。
设
m
x
l
e
n
[
i
]
mxlen[i]
mxlen[i]为作文串长度为
i
i
i的前缀能匹配到的最长后缀(这个在广义
S
A
M
SAM
SAM上跑一跑就出来了),
d
p
[
i
]
dp[i]
dp[i]表示作文串前
i
i
i位能匹配上的最大长度,
l
l
l为二分到的限制, 就有:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
i
−
j
]
+
j
)
(
j
∈
[
m
x
l
e
n
[
i
]
,
l
]
)
dp[i]=max(dp[i-1],dp[i-j]+j)(j\in [mxlen[i],l])
dp[i]=max(dp[i−1],dp[i−j]+j)(j∈[mxlen[i],l])
看起来还不是很明显? 换个写法:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
j
]
+
i
−
j
)
(
j
∈
[
i
−
m
x
l
e
n
[
i
]
,
i
−
l
]
)
dp[i]=max(dp[i-1],dp[j]+i-j)(j\in[i-mxlen[i],i-l])
dp[i]=max(dp[i−1],dp[j]+i−j)(j∈[i−mxlen[i],i−l])
发现
i
−
m
x
l
e
n
[
i
]
i-mxlen[i]
i−mxlen[i]是单调不减的,
d
p
[
j
]
−
j
dp[j]-j
dp[j]−j和
i
i
i无关, 所以维护一个单调队列即可。
设作文总长度为 S S S, 总复杂度 O ( S l o g S ) O(Slog S) O(SlogS)。
代码如下:
#include <cstdio>
#include <cmath>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define MX 3005000
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
int last, cur, cnt, l, head, tail, n, m;
int to[MX][2], par[MX], len[MX], mxlen[MX], que[MX], dp[MX];
char str[MX];
IN void insert(R int id)
{
R int now = last, tar;
if (to[now][id])
{
tar = to[now][id];
if (len[tar] == len[now] + 1) return last = tar, void();
int nw = ++cnt; len[nw] = len[now] + 1;
par[nw] = par[tar], par[tar] = nw;
std::memcpy(to[nw], to[tar], sizeof(to[nw]));
for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
return last = nw, void();
}
else
{
cur = ++cnt; len[cur] = len[now] + 1; last = cur;
for (; (~now) && !to[now][id]; now = par[now]) to[now][id] = cur;
if (now < 0) return par[cur] = 0, void();
tar = to[now][id];
if (len[tar] == len[now] + 1) return par[cur] = tar, void();
int nw = ++cnt; len[nw] = len[now] + 1;
par[nw] = par[tar], par[tar] = par[cur] = nw;
std::memcpy(to[nw], to[tar], sizeof(to[nw]));
for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
}
}
IN void find()
{
l = std::strlen(str + 1);
R int now = 0, ans = 0, id;
for (R int i = 1; i <= l; ++i)
{
id = str[i] - '0';
if (to[now][id]) ++ans, now = to[now][id];
else
{
W ((~now) && (!to[now][id])) now = par[now];
if (now < 0) now = ans = 0;
else ans = len[now] + 1, now = to[now][id];
}
mxlen[i] = ans;
}
}
IN bool check(R int lim)
{
head = 0, tail = -1;
for (R int i = 0; i < lim; ++i) dp[i] = 0;
for (R int i = lim; i <= l; ++i)
{
dp[i] = dp[i - 1];
W (head <= tail && dp[i - lim] - (i - lim) > dp[que[tail]] - que[tail]) --tail;
que[++tail] = i - lim;
W (head <= tail && que[head] < i - mxlen[i]) ++head;
if (head <= tail) dp[i] = max(dp[i], dp[que[head]] - que[head] + i);
}
return dp[l] * 10 >= l * 9;
}
int main(void)
{
scanf("%d%d", &n, &m); par[0] = -1;
for (R int i = 1; i <= m; ++i)
{
scanf("%s", str + 1);
l = std::strlen(str + 1); last = 0;
for (R int j = 1; j <= l; ++j)
insert(str[j] - '0');
}
for (R int i = 1; i <= n; ++i)
{
scanf("%s", str + 1); find();
int lef = 1, rig = l, mid, ans = 0;
W (lef <= rig)
{
mid = lef + rig >> 1;
if (check(mid)) ans = mid, lef = mid + 1;
else rig = mid - 1;
}
printf("%d\n", ans);
}
}