题目描述
对于整数序列 \((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\}\)。
题解
人类智慧题,解法是不容易想到的。
有 \(n \log n\)的线段树hash,然而有更优秀的 $ O(n) $ kmp。
考虑重新定义kmp的相等,我们记录比当前数小的最大的数(前驱)和大的最小的数(后继),如果所有的数都满足相对位置前驱处的数比它小且后继处的数比它大那么当前序列是满足的。
重写下比较函数就行。
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
int f=1,j=0;
char w=getchar();
while(!isdigit(w)){
if(w=='-')f=-1;
w=getchar();
}
while(isdigit(w)){
j=j*10+w-'0';
w=getchar();
}
return f*j;
}
const int N=1000010;
int n,m;
int p[N],h[N];
int pos[N],val[N];
int les[N],mor[N];
int nex[N];
int ans,ge[N];
struct Node
{
int next,prev;
}node[N];
bool check1(int n1,int n2)
{
bool flag=true;
if(les[n1]&&val[n2-(n1-les[n1])]>=val[n2])flag=false;
if(mor[n1]&&val[n2-(n1-mor[n1])]<=val[n2])flag=false;
return flag;
}
bool check2(int n1,int n2)
{
bool flag=true;
if(les[n1]&&h[n2-(n1-les[n1])]>=h[n2])flag=false;
if(mor[n1]&&h[n2-(n1-mor[n1])]<=h[n2])flag=false;
return flag;
}
int main()
{
n=rd(),m=rd();
for(int i=1;i<=n;i++)
{
p[i]=rd();
val[p[i]]=i;
}
for(int i=1;i<=m;i++)h[i]=rd();
for(int i=1;i<=n;i++)pos[i]=p[i];
for(int i=1;i<=n;i++)node[i].next=i+1,node[i].prev=i-1;
for(int i=n;i>=1;i--)
{
les[i]=pos[node[val[i]].prev];
mor[i]=pos[node[val[i]].next];
node[node[val[i]].next].prev=node[val[i]].prev;
node[node[val[i]].prev].next=node[val[i]].next;
}
for(int i=1;i<=n;i++)
{
int j=nex[i-1];
while(j>0&&check1(j+1,i)==false)j=nex[j];
if(check1(j+1,i)==true&&j+1<i)j++;
nex[i]=j;
}
int j=0;
for(int i=1;i<=m;i++)
{
while(j>0&&check2(j+1,i)==false)j=nex[j];
if(check2(j+1,i)==true)j++;
if(j==n)
{
j=nex[j];
ge[++ans]=i-n+1;
}
}
cout<<ans<<endl;
for(int i=1;i<=ans;i++)printf("%d ",ge[i]);
return 0;
}
代码不想写了喵