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值上:
- 字符串S的Hash值为H(S),在S后添加一个字符c构成新字符串S+c的Hash值为:H(S+c)= ( H(S)*P+val[c] ) mod M (乘P相当于P进制下的左移运算)
- 字符串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值
#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");
}
}
#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() );
}