哈希*字符串(洛谷p3370)

本文深入浅出地介绍了哈希算法的应用场景,特别是针对大量字符串去重的问题,对比了传统逐个比较方法与哈希排序的不同,并详细展示了哈希值计算的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

#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)要开始啦,希望大家踊跃参加(因为区块链用到了哈希算法)。。。。。。
### 关于洛谷 P1597 题目解析 #### 题目背景 洛谷 P1597 是一道涉及 Pascal 语言代码解析的题目。该题目要求处理一段长度不超过 255 的 Pascal 代码片段,其中仅包含赋值语句。这些赋值语句的形式为 `[变量]:=[变量或一位整数];`。未被显式赋值的变量默认初始值为 `0`。 #### 解决思路 为了完成此题,可以采用字符串解析的方式逐步提取并执行每一条赋值语句中的逻辑操作。以下是具体的实现方法: - **初始化变量表** 创建一个哈希表或者数组来存储所有可能使用的变量及其对应的数值,默认情况下所有变量初值设为 `0`[^4]。 - **逐行解析赋值语句** 对输入的每一行进行分析,识别出左侧待赋值的目标变量以及右侧表达式的具体形式(即另一个变量或单个数字)。通过正则匹配或其他方式分离目标变量名和源值。 - **更新变量状态** 将计算得到的新值存入到之前定义好的映射结构里对应位置上,从而反映最新的变量取值情况。 - **最终输出指定变量的结果** 经过上述过程之后,按照题目需求打印特定几个预定义好名称的变量当前所持有的实际数值即可满足解答条件。 #### 示例代码 下面给出了一种基于 C++ 实现的方法用于求解这个问题: ```cpp #include <bits/stdc++.h> using namespace std; int main(){ map<string, int> vars; string line; while(getline(cin,line)){ if(line.empty()) break; // 停止读取空行后的数据 size_t pos = line.find(":="); if(pos != string::npos){ string varName = line.substr(0,pos); trim(varName); // 清除多余空白字符 string valuePart = line.substr(pos+2); trim(valuePart); try{ vars[varName]=stoi(valuePart,nullptr,10); // 如果是数字直接转换成整型保存 } catch(...){ vars[varName]=vars[valuePart]; // 否则是其他已知变量的名字,则复制其值给新变量 } } } cout << "a=" << (vars.count("a")?to_string(vars["a"]):"0")<<endl; cout << "b=" << (vars.count("b")?to_string(vars["b"]):"0")<<endl; cout << "c=" << (vars.count("c")?to_string(vars["c"]):"0")<<endl; } ``` #### 注意事项 需要注意的是,在真实环境中运行这段程序前还需要加入必要的错误检测机制以应对非法输入等问题;另外也要考虑性能优化方面的事情比如减少不必要的内存分配动作等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值