【题解】BZOJ1461字符串的匹配

先说思路:
因为我们是比对较短的\(B\)与较长的\(A\)的子串,所以我们求不变的\(B\)\(next\)

对于这道题我们可以使用树状数组查询前缀和维护数的排名。

对于相同的数我们查询的排名是有误的,因此不仅要比对小于等于该数的前缀和,也要比对小于该数的前缀和。

如:对于\(A=2\) \(2\),\(B=1\) \(2\),来说,\(A\)的第二个\(2\)排名应为\(1\),但是前缀和是\(2\)

\(Next\)时需要清空数组,因为我们的\(a_2\)是与\(a_1\)开始比对的。

hack数据:

7 5 10
6 8 5 4 3 2 1
7 8 3 2 1
点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int maxn=5e6+50;

int n,k,s,next_[maxn];
int A[maxn],B[maxn],c[maxn];
int ranq[maxn],ress[maxn];
int ans,anss[maxn]; 

int lowbit(int x){
	return x&(-x);
}

void update(int x,int y){
	while(x<=s){
		c[x]+=y;
		x+=lowbit(x);
	}
}

int query(int x){
	int ans2=0;
	while(x){
		ans2+=c[x];
		x-=lowbit(x);
	}
	return ans2;
}

inline void input(){
	scanf("%d %d %d",&n,&k,&s);
	for(int i=1;i<=n;++i){
		scanf("%d",&A[i]);
	}
	for(int i=1;i<=k;++i){
		scanf("%d",&B[i]);
		update(B[i],1);
		ranq[i]=query(B[i]);
		ress[i]=query(B[i]-1);
		//cout<<"%%%"<<i<<' '<<ranq[i]<<' '<<ress[i]<<endl;
	}
}

inline void Next(){
	for(int i=2,j=0;i<=k;++i){
		while(j && (query(B[i])!=ranq[j+1] || query(B[i]-1)!=ress[j+1])){
			j=next_[j];
		}
		++j;
		next_[i]=j;
	}
}

inline void match(){
	for(int i=1,j=0;i<=n;++i){
		update(A[i],1);
		while(j &&(query(A[i])!=ranq[j+1] || query(A[i]-1)!=ress[j+1])){
			//cout<<i<<' '<<j+1<<' '<<A[i]<<' '<<query(A[i])<<' '<<ranq[j+1]<<' '<<query(A[i]-1)<<' '<<ress[j+1]<<endl;
			for(int q=i-j;q<i-next_[j];++q){
				update(A[q],-1);
				//比对相同区间把前面的删去
			}
			//cout<<"$$$"<<j<<' '<<next_[j]<<endl;
			j=next_[j];
		}
		//cout<<"###"<<i<<' '<<j<<endl;
		j++;
		if(j==k)	anss[++ans]=i-j+1;
	}
}

int main(){
	input();
	Next();
	memset(c,0,sizeof(c));
	match();
	printf("%d\n",ans);
	for(int i=1;i<=ans;++i){
		printf("%d\n",anss[i]);
	} 
	return 0;
}


posted @ 2023-07-21 09:03  Sonnety  阅读(162)  评论(3编辑  收藏  举报