P6502 [COCI2010-2011#3] ZNANSTVENIK

发布时间 2024-01-07 19:40:44作者: SunsetLake

其实直接模拟就好了。

因为要从第一行开始依次往下删,所以从小到大枚举行,看这行删完是否合法。如果不合法了,就输出答案并结束程序。然后我们就要思考如何判断当前矩阵是否合法。

一个暴力的想法是把下面的每一列字符串都表示出来,看他们之中有没有不同的。但是这样做是 \(\mathcal{O(n^2m)}\) 的,不能通过。可以发现主要是在把下面的字符串表示出来时耗费了大量时间,怎么优化呢?我们可以使用字符串哈希。

先预处理出每一列的哈希值,当我们把前 \(i\) 行删掉过后,每一列就剩下 \(i+1\)\(n\) 行了,这时它的哈希值便是 \(h_{n,j}-h_{i,j} \times p_{n-i}\),其中 \(p_i\) 是我们选的 \(base\) 值的 \(i\) 次方。这样我们就可以快速地判断是否有两列相同了,只需要看哈希值是否有重复,使用 map 即可。

如果怕被卡哈希,可以写双哈,也可以找一些奇怪的模数和 \(base\),就能过辣。

复杂度 \(\mathcal{O(nm \log n)}\)

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
char c[1005][1005];
const int mod=1e9+7;
const ll base=199;
ll h[1005][1005],p[1005];
int n,m,ans; 
map<ll,int>mp;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			cin>>c[i][j];
	p[0]=1;		
	for(int i=1;i<=n;++i)p[i]=(p[i-1]*base)%mod;
	for(int j=1;j<=m;++j){
		for(int i=1;i<=n;++i)
			h[i][j]=(h[i-1][j]*base+c[i][j]-'a')%mod;//预处理哈希数组
	for(int i=1;i<=n;++i){
		bool f=0;
		for(int j=1;j<=m;++j){
			ll now=(h[n][j]-h[i][j]*p[n-i]%mod+mod)%mod;
			if(mp[now]){//判断是否有两列相等
				f=1;
				break;
			}
			mp[now]=1;
		}
		if(!f)ans++;
		else return cout<<ans,0;
		mp.clear();
	}
	cout<<ans;
	return 0;
}