1. 程式人生 > 實用技巧 >題解 SP7022 【CPATTERN - Cow Patterns】

題解 SP7022 【CPATTERN - Cow Patterns】

本篇題解用於作者本人加深理解,也歡迎大家閱讀。

這道題的正解是$KMP$加上樹狀陣列,記錄每一個位置前幾個位置比其小的、相等的、大的數的數量,比較方式便是比較相應的數量,若相等,則匹配成功。

但是本篇題解使用了$Hash$的做法,因為$1<=s<=25$,所以我們可以利用一個數組,並利用二進位制的壓縮方式,記錄每一個數存在的位置。即:

$hash[i]$表示數字$i$在$k$的長度中出現的位置的二進位制壓縮。

如$hash[i]=1000101_2$就表示$i$在長度為$k=7$的序列內出現在了$1,5,7$的位置($or$出現在了$1 ,3,7$的位置(看個人理解)),接著再按照$s$從小到大比較二進位制壓縮,即可判斷是否一致。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=100005,M=25005,S=30,MOD=7710343;
int n,m,s,len;
int a[N],b[M];
ll ksm[M],hsa[S],hsb[S];
int ans[N],lans=0;
inline int read()
{
	char c=getchar();
	while(c<'0'||'9'<c)
	c=getchar();
	int x=0;
	while('0'<=c&&c<='9')
	{
		x*=10;
		x+=c-'0';
		c=getchar();
	}
	return x;
}
int main()
{
	cin>>n>>m>>s;
	for(int i=1;i<=n;++i)
	a[i]=read();
	for(int i=1;i<=m;++i)
	b[i]=read();
	ksm[0]=1;
	for(int i=1;i<=m;++i)
	ksm[i]=ksm[i-1]*2%MOD;
	for(int i=1;i<=m;++i)
	{
		len=max(len,b[i]);
		for(int j=1;j<=s;++j)
		{
			hsb[j]=(hsb[j]*2+(b[i]==j))%MOD;
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=s;++j)
		{
			hsa[j]=(hsa[j]*2+(a[i]==j))%MOD;
		}
		if(i>=m)
		{
//			printf("%d\n",i-m+1);
			bool ok=true;
			int aa=1,bb=1,cnt=0;
			while(aa<=s&&bb<=s)
			{
				while(aa<=s&&!hsa[aa])
				++aa;
				while(bb<=s&&!hsb[bb])
				++bb;
				if(aa>s||bb>s)
				break;
				if(hsa[aa]==hsb[bb])
				{
					++cnt;
//					printf("%d %d\n",aa,bb);
				}
				else
				{
					ok=false;
					break;
				}
				++aa;
				++bb;
			}
//			printf("%d %d %d\n",cnt,len,ok);
			if(cnt==len&&ok)
			ans[++lans]=i-m+1;
			for(int j=1;j<=s;++j)
			{
				hsa[j]=((hsa[j]-ksm[m-1]*(a[i-m+1]==j))%MOD+MOD)%MOD;
			}
		}
	}
	printf("%d\n",lans);
	for(int i=1;i<=lans;++i)
	printf("%d\n",ans[i]);
	return 0;
}