洛谷傳送門
BZOJ傳送門
題目描述
阿米巴是小強的好朋友。
在小強眼中,阿米巴是一個作文成績很高的文藝青年。爲了獲取考試作文的真諦,小強向阿米巴求教。阿米巴給小強展示了幾篇作文,小強覺得這些文章怎麼看怎麼覺得熟悉,彷彿是某些範文拼拼湊湊而成的。小強不禁向阿米巴投去了疑惑的眼光,卻發現阿米巴露出了一個狡黠的微笑。
爲了有說服力地向阿米巴展示阿米巴的作文是多麼讓人覺得“眼熟”,小強想出了一個評定作文 “熟悉程度”的量化指標:L 0 .小強首先將作文轉化成一個 01 串。之後,小強蒐集了各路名家的文章,同樣分別轉化成 01 串後,整理出一個包含了 個 01 串的“ 標準作文庫 ”。
小強認爲:如果一個 01 串長度不少於 且在 標準作文庫 中的某個串裏出現過(即,它是 標準作文庫 的 某個串 的一個 連續子串 ),那麼它是“ 熟悉 ”的。對於一篇作文(一個 01 串),如果能夠把 分割成若干段子串,其中“ 熟悉 ” 的子串的 長度 總 和 不少於 A 總 長度的 ,那麼稱 是 “ 熟悉的文章 ”。 L 0 是 能夠讓 成爲 “ 熟悉的文章 ” 的 所有 的最大值 (如果不存在這樣的 ,那麼規定 L 0 =0)。
舉個例子:
小強的作文庫裏包含了如下 個字符串:
10110
000001110
有一篇待考察的作文是:
1011001100
小強計算出這篇作文 的最大值是 ,因爲待考察的作文可以視作’10110’+‘0110’+‘0’,其中’10110’和’0110’被判定爲 “ 熟悉 ” 的。而當 或是更大的時候,不存在符合題意的分割方法。所以,這篇作文的 L 0 = 4。小強認爲阿米巴作文的 L 0 值比其他同學的明顯要大。請你幫他驗證一下。
輸入輸出格式
輸入格式:
輸入文件 cheat.in 第一行是兩個整數 ,表示待檢查的作文數量,和小強的標準作文庫的行數。
接下來是 行的 01 串,表示標準作文庫。
接下來是 行的 01 串,表示 篇作文。
輸出格式:
輸出文件 cheat.out 包含 行,每一行包含一個整數,表示該篇作文的 L 0 值。
輸入輸出樣例
輸入樣例#1:
1 2
10110
000001110
1011001100
輸出樣例#1:
4
說明
對於 的測試數據,輸入文件的長度不超過 1000 字節。
對於 的測試數據,輸入文件的長度不超過 61000 字節。
對於 的測試數據,輸入文件的長度不超過 250000 字節。
對於 的測試數據,輸入文件的長度不超過 1100000 字節。
解題分析
首先還是很明顯的二分答案, 轉化爲判定性問題。
設爲作文串長度爲的前綴能匹配到的最長後綴(這個在廣義上跑一跑就出來了), 表示作文串前位能匹配上的最大長度,爲二分到的限制, 就有:
看起來還不是很明顯? 換個寫法:
發現是單調不減的, 和無關, 所以維護一個單調隊列即可。
設作文總長度爲, 總複雜度。
代碼如下:
#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);
}
}