1. 程式人生 > >51nod 1282 時鐘 (雜湊、字串的最小表示法)

51nod 1282 時鐘 (雜湊、字串的最小表示法)

題目

這裡寫圖片描述

題解

要判斷時鐘是否相同,只需將時鐘的指標排序後求出M個距離,然後看距離陣列是否是迴圈同構即可。

迴圈同構: abcd的迴圈同構有:abcd、bcda、cdba、dabc。

要判斷是否迴圈同構,可以求出距離陣列的最小表示。然後對這個最小表示陣列求一個雜湊值,判斷這個雜湊值是否相同。

最小表示就是所有迴圈同構中字典序最小的。

雜湊的話,我用的是以前用過的一個方法:將每個值離散化為一個質數,然後累成,用unsigned long long自動取模2^64. 當然這個雜湊方法不太好,時間複雜度比較高。 可以用hash=hash*133331+a[i],走一個迴圈。

AC程式碼

#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn=3e5+7;

vector<int> V;
int getid(int x)
{
    return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
}
int prime[maxn]={2,3,5,7,11};
void init()
{
    int i=5,n=13;
    while
(i<maxn) { int tmp=(int)sqrt(n+0.5),j; for(j=0;prime[j]<=tmp;j++) if(n%prime[j]==0) break; if(prime[j]>tmp) prime[i++]=n; n+=2; } } int a[505][505]; map<ull,ll> mp; set<ll> st; set<ll>::iterator ite; int s[505]; int minShow(int
L) { int i=0,j=1,k=0; while(i<L && j<L && k<L) { int t=s[i+k<L?i+k:i+k-L]-s[j+k<L?j+k:j+k-L]; if(!t) k++; else { if(t>0) i+=k+1; else j+=k+1; if(i==j) j++; k=0; } } return min(i,j); } int ans[505]; int main() { int n,m,p; init(); while(~scanf("%d%d%d",&n,&m,&p)) { V.clear(); st.clear(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { int x; scanf("%d",&x); a[i][j]=x; } sort(a[i],a[i]+m); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(j==0) continue; V.push_back((a[i][j]-a[i][j-1]+p)%p); } V.push_back((a[i][m-1]-a[i][0]+p)%p); } V.erase(unique(V.begin(),V.end()),V.end()); mp.clear(); for(int i=0;i<n;i++) { for(int j=0;j<m-1;j++) { s[j]=(a[i][j+1]-a[i][j]+p)%p; } s[m-1]=(a[i][0]-a[i][m-1]+p)%p; int pos=minShow(m); ull x=1; for(int j=0;j<m;j++) { ans[j]=s[j+pos<m?j+pos:j+pos-m]; int id=getid(ans[j]); x*=(ull)prime[id]; } mp[x]++; st.insert(x); } ll ans=0; for(ite=st.begin();ite!=st.end();ite++) { ll tmp=mp[(*ite)]; ans+=tmp*(tmp-1)/2; } printf("%lld\n",ans); } return 0; }