题解 P4696 [CEOI2011]Matching

这道题挺妙的。

首先我们需要一个数组 \(a\),存的是转化后的 \(p\) 数组。例如,我们将序列 \({2,1,5,3,4}\) 转化为 \({2,1,4,5,3}\) ,分别对应的是 \(h\) 数组中的 \({6,3,8,1,2,7}\)

实现如下:


	for(int i=1;i<=n;i++)
	{
		p[i]=read();
		a[p[i]]=i;
		pre[i]=i-1;
		net[i]=i+1;
		
	}

然后我们维护一个双向链表,用它来求比序列中的某个数大的最小位置和比它小的最大位置。那么,为什么呢?

因为我们考虑一个序列是否合法,不用枚举整个。只需要判断每一个数是否比它前一个位置大,后一个位置小。即大于它的前驱,小于它的后继。

之后就是一个类似 kmp 的思路。

在 kmp 的板子中,我们比对的是子串与要匹配的串是否相同。而现在我们判断的是该子串是否合法。

其余与 kmp 基本一致。

	int j=0;
	for(int i=2;i<=n;i++)
	{
		while(j>0 and !check(a,j+1,i)) j=kmp[j];
		if(check(a,j+1,i)) j++;
		kmp[i]=j;
	}
	j=0;
	for(int i=1;i<=m;i++)
	{
		while(j>0 and !check(h,j+1,i)) j=kmp[j];
		if(check(h,j+1,i)) j++;
		if(j==n)
		{
			qwq[++ans]=i-n+1;
			j=kmp[j];
		}
	}
    

其余请自行实现。如果有问题,可以私信我要完整代码。

posted @ 2022-06-21 17:30  zplqwq  阅读(66)  评论(0)    收藏  举报