1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——POJ3349 Snowflake Snow Snowflakes(最小表示法+字串hash)

藍書(演算法競賽進階指南)刷題記錄——POJ3349 Snowflake Snow Snowflakes(最小表示法+字串hash)

題目:POJ3349.
題目大意:給定雪花可以用六元組 ( a 1 , a 2 ,

. . . , a 6 ) (a_1,a_2,...,a_6) 來表示,現在要求你判斷是否有兩片雪花相同.兩片雪花相同定義為它們從任意一點開始順時針或逆時針數的六元組完全相同.

我們發現這種同構類的問題很自然地可以想到最小表示法,然後順逆時針都可以很容易想到把原串和翻轉後的串的最小表示法都存起來.

現在我們的問題就變成了如何判斷是否有串相同,那麼很容易想到這可以用字串hash來做,然後就可以 O ( n )

O(n) 解決這個問題了.

程式碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
  using namespace std;

#define Abigail inline void
typedef long long LL;
typedef unsigned long long ULL;

const int L=6,N=100000;
const ULL P=13333331,M=1000037;

ULL tmp[L*2+9];

void minimum(ULL *c,int len){
  for (int i=1;i<=len;++i)
    tmp[i]=c[i],tmp[i+len]=c[i];
  int i=1,j=2,k;
  while (i<len&&j<len){
  	for (k=0;tmp[i+k]==tmp[j+k];++k);
  	tmp[i+k]<tmp[j+k]?j=j+k+1:i=i+k+1;
  	if (i==j) ++j;
  }
  if (i>j) i=j;
  for (j=1;j<=len;++j)
    c[j]=tmp[i+j-1];
}

ULL Hash(ULL *s,int len){
  ULL ans=0;
  for (int i=1;i<=len;++i)
    ans=ans*P+s[i];
  return ans;
}

struct Hash_table{
  int h[M+9];
  ULL v[M+9];
  
  void add(ULL &a,const ULL &b){a+=b;if (a>=M) a-=M;}
  
  void insert(ULL x){
  	ULL t=x;
  	for (x%=M;h[x]&&v[x]^t;add(x,5));
  	++h[x];v[x]=t;
  }
  
  bool find(ULL x){
  	ULL t=x;
  	for (x%=M;h[x]&&v[x]^t;add(x,5));
  	return h[x]?1:0;
  }
  
}h;

void reversal(ULL *c,ULL *s,int len){
  for (int i=1;i<=len;++i)
    s[i]=c[len-i+1];
}

int n,len=6;
ULL c1[L+9],c2[L+9];

Abigail getans(){
  int flag=0;
  ULL t1,t2;
  scanf("%d",&n);
  for (int i=1;i<=n;++i){
    for (int j=1;j<=6;++j)
      scanf("%llu",&c1[j]);
    reversal(c1,c2,len);
    minimum(c1,len);minimum(c2,len);
    t1=Hash(c1,len);t2=Hash(c2,len);
    if (h.find(t1)||h.find(t2)) flag=1;
    h.insert(t1);h.insert(t2);
  }
  puts(flag?"Twin snowflakes found.":"No two snowflakes are alike.");
}

int main(){
  getans();
  return 0;
}