参考了 神仙yyb的博客
现在发现kmp不仅能匹配字符串,还可以用于处理任意模式匹配中的状态,如这题中已经匹配的序列中的数的大小关系就是一种状态,使用kmp找到模式序列的每一个前缀的border,即一个最长的前缀和后缀使得它们的数字大小关系相同,即匹配状态相同,然后在失配时跳转到一个前缀的状态继续匹配,就可以解决这个问题啦!
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000007 4 int sum[N],n,m; 5 void modify(int x,int d) 6 { 7 for(;x<=m;x+=x&-x) 8 sum[x]+=d; 9 } 10 int query(int x) 11 { 12 int ans=0; 13 for(;x;x-=x&-x) 14 ans+=sum[x]; 15 return ans; 16 } 17 int read() 18 { 19 char c; 20 int x; 21 while((c=getchar())<48||c>57); 22 x=c-48; 23 while((c=getchar())>=48&&c<=57) 24 x=x*10+c-48; 25 return x; 26 } 27 int rk[N],nxt[N],a[N],b[N],p[N]; 28 vector<int> ans; 29 int main() 30 { 31 int i; 32 scanf("%d%d",&n,&m); 33 for(i=1;i<=n;i++) 34 p[read()]=i; 35 for(i=1;i<=n;i++) 36 { 37 modify(p[i],1); 38 rk[i]=query(p[i]); 39 } 40 memset(sum,0,sizeof(sum)); 41 nxt[1]=0; 42 int last=2; 43 for(i=2;i<=n;i++) 44 { 45 int len=nxt[i-1]; 46 while(len&&query(p[i])+1!=rk[len+1]) 47 { 48 len=nxt[len]; 49 while(last<i-len)modify(p[last++],-1); 50 } 51 if(query(p[i])+1==rk[len+1]) 52 { 53 nxt[i]=len+1; 54 modify(p[i],1); 55 } 56 else nxt[i]=0; 57 } 58 for(i=1;i<=m;i++) 59 a[i]=b[i]=read(); 60 sort(b+1,b+m+1); 61 for(i=1;i<=m;i++)a[i]=lower_bound(b+1,b+m+1,a[i])-b; 62 memset(sum,0,sizeof(sum)); 63 int len=0; 64 last=1; 65 for(i=1;i<=m;i++) 66 { 67 while(len&&query(a[i])+1!=rk[len+1]) 68 { 69 len=nxt[len]; 70 while(last<i-len)modify(a[last++],-1); 71 } 72 if(query(a[i])+1==rk[len+1]) 73 { 74 len++; 75 modify(a[i],1); 76 if(len==n)ans.push_back(i-len+1); 77 } 78 else len=0; 79 } 80 int s=ans.size(); 81 printf("%d ",s); 82 for(i=0;i<s;i++) 83 printf("%d ",ans[i]); 84 printf(" "); 85 return 0; 86 }