1. 程式人生 > >【bzoj2384】[Ceoi2011]Match 特殊匹配條件的KMP+樹狀數組

【bzoj2384】[Ceoi2011]Match 特殊匹配條件的KMP+樹狀數組

esp 數組 scanf 題目 bound lower 表示 highlight tle

題目描述

給出兩個長度分別為n、m的序列A、B,求出B的所有長度為n的連續子序列(子串),滿足:序列中第i小的數在序列的Ai位置。

輸入

第一行包含兩個整數n, m (2≤n≤m≤1000000)。
第二行包含n個整數si,構成1,2,…,n的排列,1≤si≤n且si≠sj。
第三行包含m個整數hi,表示建築的高度(1≤hi≤109,1≤i≤m),所有的hi均不相同。
每一行的整數之間用單個空格隔開。

輸出

第一行包含1個整數k ,表示匹配的序列數目。
第二行包含k個整數,分別為在正確匹配的每個序列中與標誌編號1 的條紋相對應的第1 棟建築的編號。這些數字按升序排列,用空格隔開。如果k=0 ,第二行為空行。

樣例輸入

5 10
2 1 5 3 4
5 6 3 8 12 7 1 10 11 9

樣例輸出

2
2 6


題解

特殊匹配條件的KMP+樹狀數組

考慮:序列滿足條件可以由 每個數前面比它小的數的個數 判定。

於是我們可以先預處理出每個數前面比它小的數應該有多少個。

然後如果暴力匹配的話肯定會TLE,於是想到KMP算法。

所以需要先求出next數組。

考慮KMP求next數組的過程:當滿足條件時從前一個遞推到後一個。那麽可以使用樹狀數組維護比一個數小的數的個數,當當前小於該數的數的個數不等於應有的個數時就減少長度,並暴力將減掉的數從樹狀數組中刪除。

由於每次next減少對應的是前面的next的增加,而next每次只增加1,因此對於每個字符的均攤時間復雜度是$O(\log m)$的。

然後求出next數組後就是匹配的過程,和求next類似,需要離散化。

因此總的時間復雜度為$O((n+m)\log m)$。貌似本題還有線性做法,然而不會= =

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1000010
int m , a[N] , s[N] , v[N] , h[N] , t[N] , next[N] , f[N] , sta[N] , tot;
inline void add(int x , int a)
{
	int i;
	for(i = x ; i <= m ; i += i & -i) f[i] += a;
}
inline int query(int x)
{
	int i , ans = 0;
	for(i = x ; i ; i -= i & -i) ans += f[i];
	return ans;
}
int main()
{
	int n , i , j , p = 0;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , s[a[i]] = i;
	for(i = 1 ; i <= n ; i ++ ) v[i] = query(s[i]) , add(s[i] , 1);
	for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &h[i]) , t[i] = h[i];
	memset(f , 0 , sizeof(f));
	for(i = 2 ; i <= n ; i ++ )
	{
		while(query(s[i]) != v[p + 1])
		{
			for(j = i - p ; j < i - next[p] ; j ++ ) add(s[j] , -1);
			p = next[p];
		}
		next[i] = ++p , add(s[i] , 1);
	}
	sort(t + 1 , t + m + 1);
	memset(f , 0 , sizeof(f));
	p = 0;
	for(i = 1 ; i <= m ; i ++ )
	{
		h[i] = lower_bound(t + 1 , t + m + 1 , h[i]) - t;
		while(p == n || query(h[i]) != v[p + 1])
		{
			for(j = i - p ; j < i - next[p] ; j ++ ) add(h[j] , -1);
			p = next[p];
		}
		p ++ , add(h[i] , 1);
		if(p == n) sta[++tot] = i - n + 1;
	}
	printf("%d\n" , tot);
	for(i = 1 ; i < tot ; i ++ ) printf("%d " , sta[i]);
	if(tot) printf("%d" , sta[tot]);
	return 0;
}

【bzoj2384】[Ceoi2011]Match 特殊匹配條件的KMP+樹狀數組