康拓展开-排列的hash
生活随笔
收集整理的這篇文章主要介紹了
康拓展开-排列的hash
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
對于一個集合內所有元素的排列,康拓展開是一個無沖突的hash法。其規則便是將排列在邏輯上排好序,然后每個排列的序號即是hash值。
關鍵就在如何快速求出序號和快速還原啦。
首先我們確定一好集合內各元素的大小關系,然后開始處理。
生成:
對于一個排列(長度為n),我們要算出它前面有多少比它小的序列,如果序號從0開始,那么這個數字就是它的序號。
有點類似數位DP的處理,我們從最高位看起(設位x),如果一個排列的最高位比它小,那么這個排列一定比它小。
所以設集合中比x小的元素有k個,如果最高位確定,那么后面的幾位可以隨意排列,顯然有(n-1)!種,那么一共就有k*(n-1)!種。
最高位確定了,我們就考慮最高位相等時次高位的情況,處理方法是類似的,但是在計算k的時候,因為原先用過的數字已經不能出現在后面了,所以統計比x'小的元素時不能把他們算進去,然后乘上(n-2)!即可。
每一位都這樣處理,就可以不重不漏啦。
復原:
因為不存在沖突,在系數k不超過n-i時,這個多項式的值我們可以用除法和取余來實現復原。
設hash值為val
k=val/(n-1)!
val%=(n-1)!//寫成val-=k*(n-1)!也是可以的
k即是比當前位小的元素個數,val把當前項減去。
即可還原排列了。
代碼:
class cantor
{
public:
#define siz 6
char c[siz]= {'','','','','',''};
LL w[siz];
bool vis[siz];
cantor()
{
w[]=;
for(int i=; i<siz; i++)
w[i]=w[i-]*i;
}
void init()
{
for(int i=; i<siz; i++)
vis[i]=false;
}
LL makeCanto(string s)
{
init();
LL rec=;
for(int i=; i<siz; i++)
{
int d=;
for(int j=; j<siz; j++)
{
if(vis[j])
continue;
if(c[j]!=s[i])d++;
else
{
vis[j]=true;
break;
}
}
rec+=w[siz-i-]*d;
}
return rec;
}
string recover(LL val)
{
init();
string s="";
for(int i=siz-; i>=; i--)
{
LL te=val/w[i];
val%=w[i];
for(int j=,cnt=-; j<siz; j++)
{
if(vis[j])continue;
else cnt++;
if(cnt==te&&!vis[j])
{
s+=c[j];
vis[j]=true;
break;
}
}
}
return s;
}
} ;
總結
以上是生活随笔為你收集整理的康拓展开-排列的hash的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#操作Excel文件
- 下一篇: .netcore centos环境搭建实