pat学习之hash复习

本文介绍了散列函数在字符串处理中的应用场景,如查询、统计和字符串转换。详细讲解了直接对应法、除留余数法等散列函数构造方式,并探讨了线性扫描法和平方探查法解决冲突的策略。同时,通过实例展示了如何利用散列来统计字符串中字符的出现次数,以及在特定问题中如检查键盘按键状态、找出唯一成绩的学生数量等的应用。此外,还提到了字符串哈希的计算方法,以及在不同问题中如何灵活运用散列技术来提高效率。
摘要由CSDN通过智能技术生成

hash题型:
1.一般用于查询类的题目,hash可以看作保存数据的结构,需要查询数字的时候可以使用
2.统计个数的题目

1.hash最简单的例子是查询数字,记录数字出现的个数。
算法笔记中对散列定义浓缩为一句话:“将元素通过一个函数转换为一个整数,这个整数唯一的代表这个元素”。,那么在查询中直接进行查询就比较方便。
2.关于散列函数:
(1)直接对应法
(2)线性变换(很少用),在(x,y)坐标对应于一个数的时候可以用到
key=x*range+y;
(3)除留余数法:key=key%mod;
可以覆盖0-mod这个范围的数,一般为了尽可能的覆盖范围里面的每一个数,会选择素数
3.解决冲突的方法
(1)线性扫描法:从当前位置向前扫描,若遇到第一个空位返回即可,若是回到原位证明没有空位。

int LinearP(vector<int>&a,int pos,int n)//a是散列数组,pos是应该在的位置,n是表长 
{
	int k=1;
	if(a[pos]==-1) return pos;//-1说明这个位置是空的
	int pos_n=(pos+k)%n; 
	while((pos_n!=pos)
	{
		if(a[pos_n]==-1) return pos_n;
		k++;
		pos_n=(pos+k)%n;
	 } 
	return -1;//代表没有找到 
 } 

2(平方探查法):假设当前位置为key,对key+1^ 2 key-1^ 2 key+2^ 2 key-2^2依次进行探测,如果是整数超过表长,取模即可
负数的话:(取模+size)%size取模

int QuadraticP(vector<int>&a,int pos,int n)
 {
 	int k=1;
 	if(pos==-1) return pos;
 	int pos_n=(pos+k)%n;
 	while(pos_n!=pos)
 	{
 		if(a[pos_n]==-1)return pos_n;
 		k=k*k;//探索正方向 
 		pos_n=(pos+k)%n;
	 }
	 k=1;
	 pos_n=((pos-k)%n+n)%n;
	 while(pos_n!=pos)
	 {
	 	if(a[pos_n]==-1)return pos_n;
 		k=k*k;//探索负方向 
 		pos_n=((pos-k)%n+n)%n;
	 }
	 return -1;//没找到 
	 //两个计算可以放到一起,先检测正方向的,然后检测反方向的,结束条件不变。
 }

3.拉链法:将产生冲突的元素,用一张链表保存起来

字符串hash

简单来说,就是将一个字符串转为一个唯一的数字对应。
假设字符串是由大写字母,小写字母,数字组成,实现方法是将其看作62进制,其中大写字母(0-25),小写字母(26-51)数字(52-61)
代码实现如下

int change(string a)
 {
 	int rel=0;
 	int n=a.length();
 	for(int i=0;i<n;i++)
 	{
 		if(a[i]>='A'&&a[i]<='Z')
 		{
 			rel=rel*62+(a[i]-'A');//为什么这么写,相当于将0位看为最高位 
		 }
		 else if(a[i]>='a'&&a[i]<='z')
		 {
		 	rel=rel*62+(a[i]-'a')+26;
		  } 
		  else
		  {
		  	rel=rel*62+(a[i]-'0')+52;
		  }
	 }
	 return rel;
 }

若数字只出现在字符串末尾,可以在最后拼接上字符串;
id=id*10+s[n-1]-‘0’;
给出N个字符串,再给出M个查询字符串,问M个字符串在N中出现的次数,
注意字符串的长度不能太长,否则数组放不下

int n,m;
	int hash[10000]={0}; 
	cin>>n>>m;
	string s;
	for(int i=0;i<n;i++)
	{
		cin>>s;
		int key=change(s);
		hash[key]++;
		cout<<key<<endl; 
	}
	for(int i=0;i<m;i++)
	{
		cin>>s;
		int key=change(s);
		cout<<hash[key]<<endl;
	}

1084 Broken Keyboard
检查数字键,字母键,和空格键是否坏掉。
由于结果最后输出大写,可以将小写字母全部转为大写字母,若存在该字母,则hash值++,hash[200]即可

string s,s1;
	cin>>s>>s1;
	int hash[130]={0}; 
	for(int i=0;i<s.length();i++)//将小写字母看为大写字母处理 
	{
		if(s[i]>='a'&&s[i]<='z')
		{
			hash[s[i]-32]++;
		}
		else
		{
			hash[s[i]]++;
		}
	 } 
	for(int i=0;i<s1.length();i++)
	{
		if(s1[i]>='a'&&s1[i]<='z')
		{
			hash[s1[i]-32]--;
		}
		else
		{
			hash[s1[i]]--;
		}
	}
	for(int i=0;i<s.length();i++)
	{
		if(s[i]>='a'&&s[i]<='z'&&hash[s[i]-32]>0)
		{	
			printf("%c",s[i]-32);
			hash[s[i]-32]=0;
		}
		else if(hash[s[i]]>0)
		{
		  printf("%c",s[i]);
		  hash[s[i]]=0;
		}
	}
	

1033 旧键盘打字
注意点:第一行可能为空行,所以用cin会有一个测试点过不去,cin读取不了空行,getline可以读取空行

string s,s1;
	int hash[130]={0};
     getline(cin,s);
	 getline(cin,s1); 
	bool flag=true;
	for(int i=0;i<s.length();i++)
	{
		if(s[i]=='+')
		flag=false;
		if(s[i]>='a'&&s[i]<='z')
		{
		hash[s[i]]++;
		hash[s[i]-32]++;
	    }
	    else if(s[i]>='A'&&s[i]<='Z')
	    {
	    	hash[s[i]]++;
	    	hash[s[i]+32]++;
		}
		else
		{
			hash[s[i]]++;
		}
	 } 
	 for(int i=0;i<s1.length();i++)
	 {
	 	if(s1[i]>='A'&&s1[i]<='Z'&&flag==true&&hash[s1[i]]==0)
	 	{
	 		printf("%c",s1[i]);
		 }
		 else if(hash[s1[i]]==0&&(s1[i]<'A'||s1[i]>'Z'))
		 {
		 	printf("%c",s1[i]);
		 }
	 }
	cout<<endl;

1038 统计同成绩学生
简单的hash

int main() {
  int hash[102]={0};
  int n,k;
  cin>>n;
  for(int i=0;i<n;i++)
  {
  	int input;
  	cin>>input;
  	hash[input]++;
   } 
   cin>>k;
   for(int i=0;i<k;i++)
   {
   	int check;
   	cin>>check;
   	if(i==0)
   	printf("%d",hash[check]);
   	else
   	printf(" %d",hash[check]);
   }
	system("pause");
	
}

1092 To Buy or Not to Buy
记录商店的颜色,用hash开始查找,若存在则使need-1;若need为0,代表全部都有,若need不为0,则代表还少这么多

string s,s1;
	getline(cin,s);
	getline(cin,s1);
	int hash[130]={0};
	int n=s.length();
	int n1=s1.length();
	int need=n1;
	for(int i=0;i<n;i++)
	{
		hash[s[i]]++;
	}
	for(int i=0;i<n1;i++)
	{
		if(hash[s1[i]]>0)
		{
			hash[s1[i]]--;
			need--; 
		}
	}
	if(need==0)
	{
		cout<<"Yes"<<" "<<n-n1; 
	}
	else
	{
		cout<<"No"<<" "<<need;
	}

最多出现的字母
注意:
1.不区分大小写
2.出现次数相同按照字典顺序比较,直接比较字母即可

string s;
	getline(cin,s);
	int max=0;//动态统计最大最小值 
	int hash[130]={0};
	for(int i=0;i<s.length();i++)//只处理大小写字母 
	{
		if(s[i]>='A'&&s[i]<='Z')//由于只输出小写字母,将大写字母当作小写字母处理 
		{
			hash[s[i]+32]++;
			if(hash[s[i]+32]>hash[max])
			{
				max=s[i]+32;
			}
			else if(hash[s[i]+32]==hash[max]&&s[i]+32<max)//若相等按照字典顺序处理 
			{
				max=s[i]+32;
			}
		 }
		 else if(s[i]>='a'&&s[i]<='z')
		 {
		 	hash[s[i]]++;
		 		if(hash[s[i]]>hash[max])
			{
				max=s[i];
			}
			else if(hash[s[i]]==hash[max]&&s[i]<max)
			{
				max=s[i];
			}
		  } 
	}
	printf("%c %d",max,hash[max]);

1043 输出PATest
复习的时候用了while1的写法,在循环里面判断这几个字母是否都是0
之前写的时候用了两层for循环,这几个字母最多就是输入字符串的长度


	char input[6]={'P','A','T','e','s','t'};
	string s;
	getline(cin,s);
	int hash[130]={0};
	for(int i=0;i<s.length();i++)
	{
		hash[s[i]]++;
	}
	for(int i=0;i<s.length();i++)//字母个数最多就这么多 
	{
		for(int j=0;j<6;j++)
		{
			if(hash[input[j]]>0)
			{
				printf("%c",input[j]);
				hash[input[j]]--;
			}
		}
		
	}

1047编程团体赛

	int n;
	cin>>n;
	
	int hash[1200]={0};
	int max=0;
	
	for(int i=0;i<n;i++)//动态记录最大的编号 
	{
		int term,id,grade;
		scanf("%d%d%d",&term,&id,&grade);
		hash[term]+=grade;
		if(hash[term]>hash[max])
		{
			max=term;
		}
		
	 } 
	cout<<max<<" "<<hash[max]<<endl;
	

1041 Be Unique

int n;
	cin>>n;
	vector<int> input;
	int hash[10010]={0}; 
	for(int i=0;i<n;i++)//统计个数
	{
		int k;
		scanf("%d",&k);
		input.push_back(k);
		hash[k]++;
	}
	int i;
	for( i=0;i<n;i++)
	{
		if(hash[input[i]]==1)
		{
			printf("%d",input[i]);
			break;
		}
	}
	if(i==n)
	cout<<"None";

1050 String Subtraction

string s,s1;
	getline(cin,s);
	getline(cin,s1);
	int hash[130]={0};
	for(int i=0;i<s1.length();i++)
	{
		hash[s1[i]]=1;
	}
	for(int i=0;i<s.length();i++)
	{
		if(hash[s[i]]==0)
		{
			printf("%c",s[i]);
		}
	}

1005 继续(3n+1)猜想
注意的是,给出的数字不在已经计算的队列中

bool cmp(int a,int b)
 {
 	return a>b;
 }
int main()
{
	
	int n;
	vector<int> a;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		int k;
		scanf("%d",&k);
		a.push_back(k);
	}
	int hash[30000]={0};
	for(int i=0;i<n;i++)
	{
		int q=a[i];
		while(q!=1)
		{
			if(q%2==0)
			{
				q=q/2;
				hash[q]=1;
			}
			else
			{
				q=(3*q+1)/2;
				hash[q]=1;
			}
		}
	}
	sort(a.begin(),a.end(),cmp);
	int count=0;
	for(int i=0;i<n;i++)
	{
		if(count==0&&hash[a[i]]==0)
		{
			printf("%d",a[i]);
			count=1;
		}
		else if(hash[a[i]]==0)
		{
			printf(" %d",a[i]);
		}
	}

1048 Find Coins
本题目存在三种方法
1.二分法
2.twopointers法
3.hash法
本次用hash的方法实现,剩下的两种方法在twopointers方法中实现

int n,sum;
	cin>>n>>sum;
	int hash[2000]={0};
	vector<int> a;
	for(int i=0;i<n;i++)
	{
		int k;
		scanf("%d",&k);
		a.push_back(k);
		hash[k]++;
	}
	sort(a.begin(),a.end(),cmp);
	bool flag=true;
	for(int i=0;i<n;i++)//循环遍历每一个数字,看是否有一个数字可以和其组成和,但其实可以只遍历其中的一半 
	{
		int q=sum-a[i];
		hash[a[i]]--;//防止相同的数字之和比如14 7+7 但是7可能只有一个 
		if(hash[q]>0)
	    {
	    	printf("%d %d",a[i],q);
	    	flag=false;
	    	break;
		}
	}
	if(flag)
	{
		cout<<"No Solution"<<endl;
	 } 
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值