最近遇到了好几道字符串比较的题目,感觉直接比较还是挺耗时间的,于是想到了哈希,但是又觉得运算的时候可能会把int数据类型爆掉,所以在讨论哈希之前还是总结一下快速幂。
1、Quickpow
这是个模板,我总结了一下网络上的各种写法,比这短的反正我是不会的:
int quickpow(int a,int b,int c) { // return (a^b)%c ---------- 把a的类型声明改成long long也许会更好
int ret = 1;
a %= c;
while(b){
if(b&1) ret = (ret*a) % c;
b >>= 1;
a = (a*a) % c;
}
return ret;
}
因为这里用到了分治的思想,所以用递归代码是很容易实现的,但是我并不推荐使用,很容易就TLE了:
int quickpow_2(int a,int b,int c) {
int ret = 1;
a %= c;
ret = (quickpow_2(a,b/2,c) * quickpow_2(a,b/2,c)) %c
if(b&1){
ret = (ret * a) % c;
}
return ret;
}
2、Hash
重头戏当然就是哈希函数,这里用到了一个大数和一个大质数用来取模,双关键字比较:
void str_hash(char *s,int &d1,int &d2){ //d1,d2为用于比较的关键字 ---------- sys为进制数
int i,len = strlen(s);
for(i = 0;i < len;++ i) {
d1 = (d1 + (s[i]-'A')*quickpow(sys,i,mod1)) % mod1;
d2 = (d2 + (s[i]-'A')*quickpow(sys,i,mod2)) % mod2;
}
}
利用进制之间的转化公式,很容易得到我们想要的十进制数,也就是代表该字符串的两个数字。
至于为什么是两个嘛……你会发现只用一个的话,在数据范围比较大的时候容易出现同一个十进制数要表示两个并不相等的字符串,虽然也可以用在哈希表上面挂链表的方式解决,但是对我个人而言是太麻烦了,毕竟咱oier还是不要写太复杂的代码……
3、All
经过上述讨论,就可以写出完整代码了!!!(Ctrl + C党的福利):
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int sys = 26; //假定字符串只含 A ~ Z,即26进制
const int maxn = 3000; //假定字符串长度小于等于100
const int mod1 = 100000007; //一个常用的取模大数
const int mod2 = 299999321; //一个大质数
char a[maxn];
int ha[maxn][2],n;
int quickpow(LL a,int b,int c) { // return (a^b)%c
int ret = 1;
a %= c;
while(b){
if(b&1) ret = (ret*a) % c;
b >>= 1;
a = (a*a) % c;
}
return ret;
}
void str_hash(char *s,int &d1,int &d2){ //d1,d2为用于比较的关键字
int i,len = strlen(s);
for(i = 0;i < len;++ i) {
d1 = (d1 + (s[i]-'A')*quickpow(sys,i,mod1)) % mod1;
d2 = (d2 + (s[i]-'A')*quickpow(sys,i,mod2)) % mod2;
}
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++ i){
scanf("%s",a);
str_hash(a,ha[i][0],ha[i][1]); //读入并处理字符串
//注意,这里并没有保存每个字符串,但是实际运用的时候是需要保存的,毕竟还要输出符合条件的字符串
}
for(int i = 1;i <= n;++ i){
printf("%d %d\n",ha[i][0],ha[i][1]); //输出哈希表
}
return 0;
}