题解 P4696 【[CEOI2011]Matching】
kmp搞一搞
找到转化后的p排列,对于每个位置,之前比它大的最小的位置和比它小的最大的位置。
然后就kmp搞一搞就好啦。只不过原来的kmp是判断字母相同,现在是判断放这个数合不合法,其余的相同。
这个方法是线性的
#include<cstdio>
const int N=1e6+50;
inline void rad(int &_){
static char ch;
while(ch=getchar(),ch<'0'||ch>'9');_=ch-48;
while(ch=getchar(),ch<='9'&&ch>='0')_=_*10+ch-48;
}
int Out[10],out;
inline void prt(int u){
if(!u)++out;
else for(;u;Out[++out]=u%10,u/=10);
for(;out;putchar(48+Out[out--]));
putchar(' ');
}
int n,m,x,ans;
int a[N],b[N],c[N],kmp[N];
int pre[N],net[N],L[N],R[N],Ans[N];
inline bool get(int *p,int u,int v){
return (!L[u]||p[v+L[u]]<p[v])&&(!R[u]||p[v+R[u]]>p[v]);
//判断合不合法
}
int main(){
rad(n);rad(m);
for(int i=1;i<=n;i++){
rad(b[i]);a[b[i]]=i;
pre[i]=i-1;net[i]=i+1;
}
//a[]即为转化后的p数组
for(int i=n;i>=1;i--){
x=a[i];
if(pre[x])L[i]=b[pre[x]]-i;
if(net[x]<=n)R[i]=b[net[x]]-i;
pre[net[x]]=pre[x];
net[pre[x]]=net[x];
}
//双向链表求比它大的最小的位置和比它小的最大的位置
x=0;
for(int i=2;i<=n;i++){
while(x&&!get(a,x+1,i))x=kmp[x];
if(get(a,x+1,i))x++;
kmp[i]=x;
}
//先自身匹配
x=0;
for(int i=1;i<=m;i++)rad(c[i]);
for(int i=1;i<=m;i++){
while(x&&!get(c,x+1,i))x=kmp[x];
if(get(c,x+1,i))x++;
if(x==n)Ans[++ans]=i-n+1,x=kmp[x];
}
//然后与h串匹配
prt(ans);putchar('\n');
for(int i=1;i<=ans;i++)prt(Ans[i]);
}