[Luogu P4022] [BZOJ 2806] [CTSC2012]熟悉的文章

洛谷傳送門

BZOJ傳送門

題目描述

阿米巴是小強的好朋友。

在小強眼中,阿米巴是一個作文成績很高的文藝青年。爲了獲取考試作文的真諦,小強向阿米巴求教。阿米巴給小強展示了幾篇作文,小強覺得這些文章怎麼看怎麼覺得熟悉,彷彿是某些範文拼拼湊湊而成的。小強不禁向阿米巴投去了疑惑的眼光,卻發現阿米巴露出了一個狡黠的微笑。

爲了有說服力地向阿米巴展示阿米巴的作文是多麼讓人覺得“眼熟”,小強想出了一個評定作文 “熟悉程度”的量化指標:L 0 .小強首先將作文轉化成一個 01 串。之後,小強蒐集了各路名家的文章,同樣分別轉化成 01 串後,整理出一個包含了 MM 個 01 串的“ 標準作文庫 ”。

小強認爲:如果一個 01 串長度不少於 LL 且在 標準作文庫 中的某個串裏出現過(即,它是 標準作文庫 的 某個串 的一個 連續子串 ),那麼它是“ 熟悉 ”的。對於一篇作文(一個 01 串)AA,如果能夠把 AA 分割成若干段子串,其中“ 熟悉 ” 的子串的 長度 總 和 不少於 A 總 長度的 90%90\%,那麼稱 AA 是 “ 熟悉的文章 ”。 L 0 是 能夠讓 AA 成爲 “ 熟悉的文章 ” 的 所有 LL 的最大值 (如果不存在這樣的 LL,那麼規定 L 0 =0)。

舉個例子:

小強的作文庫裏包含了如下 22 個字符串:

10110
000001110

有一篇待考察的作文是:

1011001100

小強計算出這篇作文 LL 的最大值是 44,因爲待考察的作文可以視作’10110’+‘0110’+‘0’,其中’10110’和’0110’被判定爲 “ 熟悉 ” 的。而當 L=5L = 5 或是更大的時候,不存在符合題意的分割方法。所以,這篇作文的 L 0 = 4。小強認爲阿米巴作文的 L 0 值比其他同學的明顯要大。請你幫他驗證一下。

輸入輸出格式

輸入格式:

輸入文件 cheat.in 第一行是兩個整數 N,MN, M,表示待檢查的作文數量,和小強的標準作文庫的行數。

接下來是 MM 行的 01 串,表示標準作文庫。

接下來是 NN 行的 01 串,表示 NN 篇作文。

輸出格式:

輸出文件 cheat.out 包含 NN 行,每一行包含一個整數,表示該篇作文的 L 0 值。

輸入輸出樣例

輸入樣例#1:

1 2
10110
000001110
1011001100

輸出樣例#1:

4

說明

對於 30%30\%的測試數據,輸入文件的長度不超過 1000 字節。

對於 50%50\%的測試數據,輸入文件的長度不超過 61000 字節。

對於 80%80\%的測試數據,輸入文件的長度不超過 250000 字節。

對於 100%100\%的測試數據,輸入文件的長度不超過 1100000 字節。

解題分析

首先還是很明顯的二分答案, 轉化爲判定性問題。

mxlen[i]mxlen[i]爲作文串長度爲ii的前綴能匹配到的最長後綴(這個在廣義SAMSAM上跑一跑就出來了), dp[i]dp[i]表示作文串前ii位能匹配上的最大長度,ll爲二分到的限制, 就有:
dp[i]=max(dp[i1],dp[ij]+j)(j[mxlen[i],l]) dp[i]=max(dp[i-1],dp[i-j]+j)(j\in [mxlen[i],l])
看起來還不是很明顯? 換個寫法:
dp[i]=max(dp[i1],dp[j]+ij)(j[imxlen[i],il]) dp[i]=max(dp[i-1],dp[j]+i-j)(j\in[i-mxlen[i],i-l])
發現imxlen[i]i-mxlen[i]是單調不減的, dp[j]jdp[j]-jii無關, 所以維護一個單調隊列即可。

設作文總長度爲SS, 總複雜度O(SlogS)O(Slog S)

代碼如下:

#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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章