哈希*字符串(洛谷p3370)

首先,让我们来借用这道题来了解一下哈希算法如何来寻找相同字符串:

我们从这道题中可得知:该题是很普通的一个寻找不同亦或说相同字符串的题,所以使用逐个比较的方法是可以得到正确结果的,例:

#include<iostream>
using namespace std;
string a[10002];
int n;
int ans;
bool m;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		for(int j=1;j<=i-1;j++){
			if(a[i]==a[j]){
				m=1;
				break;
			}
		}
		if(m==0){
			ans++;
		}else{
			m=0;
		}
	}
	cout<<ans;
	return 0;
}//代码极丑,大神勿喷

上述代码可以很好地解决这个问题(好吧,作者也承认这道题用这个过不去),但是此时的复杂度却远超哈希排序的复杂度(此处约等于O(n^2))而哈希排序却可以很好地缩减它的复杂度至O(nK)注:此处的K为最长字符串长度。

我们先观摩一下hash排序的伟岸:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int prime=131;
long long mod1=10099757,mod2=10099759;
int hash(char c[]) {
    int b=strlen(c);
    long int ans=0;
    long int ans1=0;
    for(int i=1; i<=b; i++) {
        ans+=(ans*prime)%mod1+(int)c[i];
        ans1+=(((int)c[i]*179)+(int)c[i])%mod2;
    }
    return ans+ans1;
}
long int e[400002];
bool d[1000002];
char a[1000002];
int main() {
    int n;
    int sum=1;
    cin>>n;
    for(int i=1; i<=n; i++) {
        scanf("%s",a);
        e[i]=hash(a);
        //	cout<<e[i]<<endl;
    }
    sort(e+1,e+n+1);
    for(int i=2; i<=n; i++) {
        if(e[i]!=e[i-1]) {
            sum++;
        }
    }
    cout<<sum;
    return 0;
}

此处使用的是单hash不是双hash,因为考虑到有的题或是在实践应用中,我们可以考虑使用3hash,因为这样可以报证重复率接近0(当然,此处你需要保证你的hash足够复杂)。

上面扯了那么多,我们现在来看一下什么是hash算法:

首先,我们会先获取几个字符串,例:

ndsisk

16546

nskivn

ndsisk

123546

cdkl

cdkl

看到上面的字符串后,要求去除重复字符串,输出剩下字符串的个数,此时大多数人会先考虑第一种算法,但是,重点来了:如果我有1000000个字符串怎么办,很明显那样的话第一个算法不经调整确实与第二个算法相形见绌,现在就说一下第二个算法的实现,首先我们需要获取这些字符串,然后我们要用一种独特的方式来获取这个字符串所映射出的哈希值,当然这个哈希值只能由唯一 一个字符串得到(不过似乎这不可能诶)例:

int hash(char a[]) {
	int len=strlen(a);
	int ans=0;
	int prime=15797;
	int e=131;
	int mod=10000004;
	for(int i=1; i<=len; i++) {
		ans+=(ans*e+prime)%mod+(int)a[i];
	}
	return ans;
}

这个“ans+=(ans*e+prime)%mod+(int)c[i];”就是为了求独特哈希值的方法。

这个哈希值可以说是相对独立却又不是完全独立的,在某种角度上来讲肯定会有另外一个字符串与其拥有相同的哈希值所以,此时我们需要再引进一个新的求哈希值的方法,之后就变成了这样:

int hash(char a[]) {
	int len=strlen(a);
	int ans=0;
	int ans1=0;
	int prime=15797;
	int e=131;
	int e1=179;
	int mod=10000004;
	int mod2=10000007;
	for(int i=1; i<=len; i++) {
		ans+=(ans*e+prime)%mod+(int)a[i];
			ans1+=(((int)a[i]*179)+(int)a[i])%mod2;
	}
	return ans+ans1;
}

这样的话可以让这个所求的哈希值更加的独立。

现在说完求哈希值的方法,而后就要用它来干一些实际上的事,例如还是刚才那到道题:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int prime=131;
long long mod1=10099757,mod2=10099759;
int hash(char c[]) {
	int b=strlen(c);
	long int ans=0;
	long int ans1=0;
	for(int i=1; i<=b; i++) {
		ans+=(ans*prime)%mod1+(int)c[i];
		ans1+=(((int)c[i]*179)+(int)c[i])%mod2;
	}
	return ans+ans1;
}
long int e[400002];
bool d[1000002];
char a[1000002];
int main() {
	int n;
	int maxn=0;
	int sum=1;
	cin>>n;
	for(int i=1; i<=n; i++) {
		scanf("%s",a);
		e[i]=hash(a);
		//	cout<<e[i]<<endl;
	}
	sort(e+1,e+n+1);
	for(int i=2; i<=n; i++) {
		if(e[i]!=e[i-1]) {
			sum++;
		}
	}
	cout<<sum;
	return 0;
}

这段代码的程序其实非常易读,主旨就是讲哈希值运用快速排序排序完后统计一下出现过的不同的哈希值的数量,这就是一个很简单的哈希值排序。

2018 区块链技术及应用峰会(BTA)要开始啦,希望大家踊跃参加(因为区块链用到了哈希算法)。。。。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值