题解 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];
}
}
其余请自行实现。如果有问题,可以私信我要完整代码。

浙公网安备 33010602011771号