0x14 Hash

Hash表:一般由Hash函数(散列函数)与链表结构共同实现。

与离散化思想类似:将复杂信息映射到一个较容易维护的值域内

但可能存在两个不同信息映射为同一值,即产生冲突
一种解决方案:
建立一个邻接表结构,以Hash函数的值域作为表头数组Head,映射后值相同的信息构成一个链表接在对应表头。(可以链表中进行信息比较)

137. 雪花雪花雪花
书上的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10,P=92083;
int n,tot,snow[N][6],head[N],nxt[N],tmp[6];
int get_hash( int *a )    
{
    int res1=0,res2=0;
    for ( int i=0; i<6; i++ )
        res1=(res1+a[i])%P,res2=(ll)res2*a[i]%P;
    return (res1+res2)%P;
}
bool check( int *a,int *b )   
{
    for ( int i=0; i<6; i++ )
     for ( int j=0; j<6; j++ )
     {
        bool flag=1;
        for ( int k=0; k<6; k++ )    
            if ( a[(i+k)%6]!=b[(j+k)%6] ) flag=0;
        if ( flag ) return 1;
        flag=1;
        for ( int k=0; k<6; k++ )
            if ( a[(i+k)%6]!=b[(j-k)%6] ) flag=0;
        if ( flag ) return 1;
     }
    return 0;
}
bool insert( int *a )         
{
    int hval=get_hash(a);
    for ( int i=head[hval]; i; i=nxt[i] )     
        if ( check(snow[i],a) ) return 1;

    tot++;
    for ( int i=0; i<6; i++ )
        snow[tot][i]=a[i];
    nxt[tot]=head[hval]; head[hval]=tot;        
    return 0;
}
int main()
{
    scanf( "%d",&n );
    while ( n-- )
    {
        for ( int i=0; i<6; i++ )
            scanf( "%d",&tmp[i] );
        if ( insert(tmp) ) { printf( "Twin snowflakes found.\n" ); return 0; }
    }
    printf( "No two snowflakes are alike." );
}

其他大佬的写法

#include <bits/stdc++.h>
using namespace std;
const int K = 99991;
const int N=100000+10;
struct Node
{
    int c[7];
};
int m[N],n,ok,sum;
Node snow[N][10];//如果你写个100,你五十分,如果你写个50,恭喜你加了10分,如果你很疯狂写个20,恭喜你分数不变,如果你破釜沉舟,写个10,恭喜你Ac了,出题人是多么的有趣啊.
bool solve(Node x,Node y) 
{
    sort(x.c+1,x.c+7);//排序有利于身心健康
    sort(y.c+1,y.c+7);
    for(int i=1;i<=6;i++)
    {
        if(x.c[i]!=y.c[i])//只要不一样,就直接退出
            return false;
    }
    return true;
}
int main()
{
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        sum=0;
        Node now;
        for(int j=1; j<=6; j++)
        {
            scanf("%d",&now.c[j]);
            sum=(sum+now.c[j])%K;//取模运算也就是hash
        }
        for(int j=0; j<m[sum]; j++)//找到所有hash值是一样的雪花,然后每一个判断.
        {
            if(solve(now,snow[sum][j]))//如果这两个雪花一样大小
            {
                ok=1;//如果说hash值不一样,那么肯定不会一样,如果hash值一样,还要判断
                break;
            }
        }
        snow[sum][m[sum]]=now;//我们hash是记录这个值,以链表的方式存储,这里是静态存储,也就是数组模拟链表,我们要知道数据是死,人是活的,出题人是懒的.
        m[sum]++;
        if (ok)
            break;
    }
    if(ok)
        printf("Twin snowflakes found.\n");
    else
        printf("No two snowflakes are alike.\n");
    return 0;
}

作者:秦淮岸灯火阑珊
链接:https://www.acwing.com/solution/content/900/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

字符串Hash
将一个任意长度的字符串映射成一个非负整数,并且其冲突概率几乎为0

操作过程:
取一固定值P(一般来说,取P=131/13331,此时Hash值产生冲突的概率极低,即Hash值相同代表字符串相等),把字符串看成P进制数,并分配一个大于0的数值,代表每种字符。
取一固定值M,求出该P进制数对M的余数,作为该字符串的Hash值。(通常取M=264,直接使用unsigned long long 类型存储Hash值,在计算时可以不处理算数溢出问题,溢出时相当于自动对M取模,可以避免低效的取模运算)
还可以多取一些恰当的P,M值(大质数)多进行几组Hash运算,提高准确性。

字符串的操作可以通过Hash运算反映到Hash值上:

  1. 字符串S的Hash值为H(S),在S后添加一个字符c构成新字符串S+c的Hash值为:H(S+c)= ( H(S)*P+val[c] ) mod M (乘P相当于P进制下的左移运算)
  2. 字符串S的Hash值为H(S),字符串S+T的Hash值为H(S+T),可以求得字符串T的Hash值:
    H(T)=(H(S+T)-H(S)*Plength(T))mod M

因此我们可以用O(N)时间预处理字符串所有前缀Hash值,并在O(1)时间内查询任意子串的Hash值

例:138. 兔子与兔子

#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
unsigned long long Hash[N],p[N]; 
char s[N];
int main()
{
    scanf("%s", s+1);
    int len=strlen(s+1),m;
    p[0]=1;
    for(int i=1;i<=len;++i)
    {
        Hash[i]=Hash[i-1]*131+(s[i]-'a'+1);
        p[i]=p[i-1]*131;
    }
    scanf("%d", &m);
    while(m--)
    {
        int l1,l2,r1,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        if(Hash[r1]-Hash[l1-1]*p[r1-l1+1]==Hash[r2]-Hash[l2-1]*p[r2-l2+1])
        puts("Yes");
        else puts("No");
    }
    
}

139. 回文子串的最大长度

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=2e6+10,P=131;
ULL hash1[N],hash2[N],p[N];
ULL get_hash(ULL h[],int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1];
}
char s[N];
int main()
{
	int t=1;
	while(scanf("%s",s+1))
	{
		if(!strcmp(s+1,"END")) break; 
		int len=strlen(s+1);
		for(int i=len*2;i;i-=2)
		{
			s[i]=s[i/2];
			s[i-1]='a'+26;
		}
		p[0]=1;int n=2*len;
		for(int i=1,j=n;i<=n;++i,--j)
		{
			hash1[i]=hash1[i-1]*P+s[i];
			hash2[i]=hash2[i-1]*P+s[j];
			p[i]=p[i-1]*P;
		}
		int res=0;
		for(int i=1;i<=n;++i)//枚举中点 
		{
			int l=0,r=min(i-1,n-i);//取终点左右两边较小的区间长度 
			while(l<r)
			{
				int mid=(l+r+1)>>1;
				if(get_hash(hash1,i-mid,i-1)!=get_hash(hash2,n-(i+mid)+1,n-(i+1)+1))
				r=mid-1;
				else l=mid; 
			}
			if(s[i-l]<='z') res=max(res,l+1);
			else res=max(res,l);
		}
		cout << "Case " << t++ << ": " << res << endl;
    }
}


manacher算法O(N)
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10;
char s[N],str[N];
int pos[N];
int init()              //处理原字符串
{
        int len=strlen(s); 
        str[0]='@'; str[1]='#';         //防止越界
        int j=2;
        for ( int i=0; i<len; i++ )
                str[j++]=s[i],str[j++]='#';
        str[j]='\0'; return j;
}
int manacher()
{
        int ans=-1,len=init(),mx=0,id=0;
        for ( int i=1; i<len; i++ )
        {
                if ( i<mx ) pos[i]=min( pos[id*2-i],mx-i );             //situation1
                else pos[i]=1;                                  //situation2
                while ( str[i+pos[i]]==str[i-pos[i]] ) pos[i]++;                //扩展  
                if ( pos[i]+i>mx ) mx=pos[i]+i,id=i;            //update id
                ans=max( ans,pos[i]-1 );
        }
        return ans;
}

int main()
{
        int cnt=0;
        while ( scanf( "%s",s) && s[0]!='E' ) 
                printf( "Case %d: %d\n",++cnt,manacher() );
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值