洛谷P4696 [CEOI2011]Matching(KMP)

洛谷P4696 [CEOI2011]Matching(KMP)

題目大意

對於整數序列 \((a_1,a_2,\cdots,a_n)\)\(1\sim n\) 的排列 \((p_1,p_2,\cdots,p_n)\),稱 \((a_1,a_2,\cdots,a_n)\) 符合 \((p_1,p_2,\cdots,p_n)\),當且僅當:

  • \(\{a\}\) 中任意兩個數字互不相同;

  • \((a_1,a_2,\cdots,a_n)\) 從小到大排序後,將會得到 \((a_{p_1},a_{p_2},\cdots,a_{p_n})\)

現在給出 \(1\sim n\) 的排列 \(\{p\}\) 和序列 \(h_1,h_2,\cdots,h_m\)​​,請你求出哪些 \(\{h\}\) 的子串符合排列 \(\{p\}\)

數據範圍

對於 \(100\%\) 的數據,有 \(2\le n\le m\le 1\ 000\ 000;1\le h_i\le 10^9;1\le p_i\le n\),保證 \(\{h\}\) 中的元素互不相同,且 \(\{p\}\) 是一個排列。

解題思路

好題!考慮 KMP 和 AC 自動機本質上需要滿足什麼條件?

如果 \(s[1 \dots n]=t[1\dots n]\) 那麼 \(s[l\dots r]=t[l \dots r]\)

只要快速判斷兩個字符相等即可,但在這題中如果跳了 nxt 字符串竟是變化的!我們滿足另一個條件也可以判斷:直接判斷兩個字符串完全相等即可。

在這題當中,首先將 a 變化爲 \(a[a_i] = i\) 題目條件等價於將 h 的子串 s 排序離散化之後和 a 完全相同。

例如 \(s=\{21,45,23,7,8,4,43\}=\{4,7,5,2,3,1,6\}\) 考慮匹配跳 nxt 的時候,這個排列是變了的,但是如果最後一個字符在之前的串中的排名相同,那麼這個字符就是可以加入末尾的,即使其他數的排名變化了也是依然是相等的。

所以現在我們只需要快速判斷一個數的排名,可以先預處理出 p 這個排列中每個數的前驅和後繼,注意這裏的前驅(後繼)是位置在自己前面的第一個小於(大於)自己的數。

那麼在 h 這個串上匹配的時候,只用查看對應 p 上的前驅後繼是不是真正的前驅後繼即可。

這題另一特點就是下標巨多,請仔細考慮!

const int N = 1005000;
int rp[N], aft[N], pre[N], nxt[N], h[N], p[N], m, n;
bool cmp(int t, int *h, int x) {
	if (aft[t] != n + 1 && x > h[rp[aft[t]]]) return 0;
	if (pre[t] && x < h[rp[pre[t]]]) return 0;
	return 1;
}

int ans[N], tp;
int main() {
	read(n), read(m);
	for (int i = 1;i <= n; ++i) read(rp[i]), p[rp[i]] = i, pre[i] = i - 1, aft[i] = i + 1;
	for (int i = n;i >= 1; --i) {
		int nt = aft[p[i]], pr = pre[p[i]];
		aft[pr] = nt, pre[nt] = pr;
	}
	int j = nxt[2] = 1;
	for (int i = 3;i <= n; ++i) {
		while (j && !cmp(p[j + 1], p + i - j - 1, p[i])) j = nxt[j];
		if (cmp(p[j + 1], p + i - j - 1, p[i])) ++j;
		nxt[i] = j;
	}
	j = 0;
	for (int i = 1;i <= m; ++i) {
		read(h[i]);
		while (j && (j == n || !cmp(p[j + 1], h + i - j - 1, h[i]))) j = nxt[j];
		if (cmp(p[j + 1], h + i - j - 1, h[i])) ++j;
		if (j == n) ans[++tp] = i - n + 1; 
	}
	write(tp);
	for (int i = 1;i <= tp; ++i) write(ans[i], ' ');
	return 0;
}

/*

5 10
1 2 5 3 4
1 8 9 6 4 45 32 88 2 7

*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章