是否有一种散列技术可以 return 对字符串的反向进行与对原始字符串相同的散列?
Is there a hashing technique such that it would return the same hash for the reverse of a string as it would for the original string?
例如,对于这两个字符串,此哈希函数应该return相同的哈希值。
字符串 a = "hello"
字符串 b = "olleh"
这背后的动机是找到句子中重复的单词对。
构造 hash(min(string, reverse(string)))
应该可以解决问题。这是一个名为 canonicalization.
的技术实例
通常我们可以这样生成hash函数:
int hashCode(){
int h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
for (int i = 0; i < value.length(); i++) {
h = 31 * h + val[i];
}
}
return h;
}
所以,我们可以稍微修改一下,得到我们的哈希函数
int hashCode(){
int h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
int mid = value.length()/2;
int rate = 1;
if(value.length() % 2 == 0){
mid = (value.length()*2 - 1)/2;
rate = 2;
}
for (int i = 0; i < value.length(); i++) {
h += val[i]*Math.pow(31, Math.abs(rate*i - mid));
}
}
return h;
}
因此,我们不再以第一个字符为起点计算哈希码,而是将计算哈希码的起点移至中间字符。
对于偶数长度的字符串,我们将所有索引乘以2,因此我们可以获得中间位置的整数值。
的哈希码
hello: 213202
olleh: 213202
olelh: 213412
hlelo: 213412
更新:
我们可以观察到,对于上述方法,围绕中点对称的两个位置将被视为相等,因此,例如 "hell" 和 "lelh" 将具有相同的哈希码。
为避免这种情况,一种方法是将字符串的每一半视为两个单独的字符串,并组合它们的哈希码:
public int hashCode() {
long h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
int mid = value.length() / 2;
int rate = 1;
if (value.length() % 2 == 0) {
mid = (2*value.length() - 1)/2;
rate = 2;
}
long midPart = 0;
long lowerHalf = 0;
long upperHalf = 0;
if(value.length() % 2 != 0){
midPart = val[value.length()/2];
}
for(int i = 0; i < value.length()/2; i++){
lowerHalf += Math.pow(31, mid - rate*i)*val[i];
}
for(int i = (value.length() + 1)/2; i < value.length(); i++){
upperHalf += Math.pow(31,rate*i - mid)*val[i];
}
long max = Math.max(lowerHalf, upperHalf);
long min = Math.min(lowerHalf, upperHalf);
h = max*(long)Math.pow(31, (value.length() + 1)/2) + min + midPart;
}
return (int)h;
}
例如,对于这两个字符串,此哈希函数应该return相同的哈希值。
字符串 a = "hello" 字符串 b = "olleh"
这背后的动机是找到句子中重复的单词对。
构造 hash(min(string, reverse(string)))
应该可以解决问题。这是一个名为 canonicalization.
通常我们可以这样生成hash函数:
int hashCode(){
int h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
for (int i = 0; i < value.length(); i++) {
h = 31 * h + val[i];
}
}
return h;
}
所以,我们可以稍微修改一下,得到我们的哈希函数
int hashCode(){
int h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
int mid = value.length()/2;
int rate = 1;
if(value.length() % 2 == 0){
mid = (value.length()*2 - 1)/2;
rate = 2;
}
for (int i = 0; i < value.length(); i++) {
h += val[i]*Math.pow(31, Math.abs(rate*i - mid));
}
}
return h;
}
因此,我们不再以第一个字符为起点计算哈希码,而是将计算哈希码的起点移至中间字符。
对于偶数长度的字符串,我们将所有索引乘以2,因此我们可以获得中间位置的整数值。
的哈希码hello: 213202
olleh: 213202
olelh: 213412
hlelo: 213412
更新:
我们可以观察到,对于上述方法,围绕中点对称的两个位置将被视为相等,因此,例如 "hell" 和 "lelh" 将具有相同的哈希码。
为避免这种情况,一种方法是将字符串的每一半视为两个单独的字符串,并组合它们的哈希码:
public int hashCode() {
long h = 0;
if (h == 0 && value.length() > 0) {
char val[] = value.toCharArray();
int mid = value.length() / 2;
int rate = 1;
if (value.length() % 2 == 0) {
mid = (2*value.length() - 1)/2;
rate = 2;
}
long midPart = 0;
long lowerHalf = 0;
long upperHalf = 0;
if(value.length() % 2 != 0){
midPart = val[value.length()/2];
}
for(int i = 0; i < value.length()/2; i++){
lowerHalf += Math.pow(31, mid - rate*i)*val[i];
}
for(int i = (value.length() + 1)/2; i < value.length(); i++){
upperHalf += Math.pow(31,rate*i - mid)*val[i];
}
long max = Math.max(lowerHalf, upperHalf);
long min = Math.min(lowerHalf, upperHalf);
h = max*(long)Math.pow(31, (value.length() + 1)/2) + min + midPart;
}
return (int)h;
}