题解 Luogu P4595 POPLOCAVANJE---Hash

题面描述

题目分析

通过读题可以得知只要尽可能的把地砖铺上去然后再在统计没被铺盖的长度就是答案,所以只要把地砖可以铺的区间找出来,然后再用差分就可以做到,关于匹配字符串有几种算法: \(\operatorname{KMP}\)\(\operatorname{AC}\) 自动机 \(\operatorname{Hash}\) 还有几种其他的但是在这里就只放 \(\operatorname{Hash}\) 了 (因为数据范围

想法实现

可能很多人对哈希算法有奇怪的偏见。

但是这确实可以做到,首先来看看为什么这个方法可以做到匹配。

6
abcabc
2
abca
cab

这是其中的一个样例。

首先原先的地面是 abcabc

那么就可以把这个串转成数字串 123123

它已经是个数字了,于是就可以把它当成 \(\operatorname{prime}\) 进制数(\(\operatorname{prime}\) 应该是一个大于 \(26\) 的质数),然后就可以只用一个 \(\operatorname{int}\) 就可以存下来了。

\(\operatorname{prime}\)\(31\) 时:

应该是:

\[1\times 31^6+2\times 31^5+3\times 31^4+1\times 31^3+2\times 31^2+3\times 31^1 \]

这样就完成了字符串向数字的转换。

观察这个式子,你会发现它好像会爆!!!

这个时候你应该打开一个 Cpp-IDE,然后输入几个数字试一下,之后可以得出结论:同样的数字乘以同样的数字爆出去的结果是一样的!

如代码:

#include<bits/stdc++.h>
using namespace std;

int main(){
	int x=rand()*100000+rand(), y; y=x;
	cout<<x<<' '<<y<<endl;
	x*=31;
	cout<<x<<' '<<y<<endl;
	y*=31;
	cout<<x<<' '<<y<<endl;
	return 0;
}

在第三次输出的数字还都是一样的。

所以可以证明 \(\operatorname{Hash}\) 是可行的。

	for(int i=0;i<=1;i++)pri[i][0]=1;
	for(int i=1;i<=N;i++)
	for(int j=0;j<=1;j++){
		key[j][i]=key[j][i-1]*prime[j]+s[i];
		pri[j][i]=pri[j][i-1]*prime[j];
	}

这样就可以构建出地板了!!!

代码实现

scanf("%s",s);
int l=strlen(s),kp[2]={0,0};
for(int j=0;j<l;j++)
for(int k=0;k<=1;k++) kp[k]=kp[k]*prime[k]+s[j];
for(int j=l;j<=N;j++){
	bool checker = true;
	for(int k=0;k<=1;k++){
		int w=key[k][j]-key[k][j-l]*pri[k][l];
        //可以截出一段长度相同的区间
		if (w!=kp[k]) checker=false;
	}
	if(checker)E[j-l+1]++,E[j+1]--;
}

对于每个字符串的操作(上面)。

至此,这道题解决完毕~~~

完结撒花

Made By \(\operatorname{wheneveright}\).

posted @ 2021-04-12 19:07  XJ21  阅读(81)  评论(0)    收藏  举报