题意理解
给出一组数,数量是N,每个数转化为二进制表示,二进制的位数是K,形成一个N*K的矩阵,现在要找出一个区间i,j,满足i,j之间的每一列二进制位“1”的和相等。这个区间称为黄金平衡区间。求最长的区间长度。
问题分析
用HASH数据结构
一步转化,黄金平衡区间累计每一位的二进制值和,求出累计举证sum[N][K].
二步转化, 满足条件的区间i,j。即是满足sum[ j ][ 0 ] - sum[ i ][ 0 ] = sum[ j ][ 1 ] - sum[ i ][ 1 ] = ... = sum[ j ][ k - 1 ] - sum[ i ][ k - 1 ].
三步转化,上式等价于 sum[ j ][ 1 ] - sum[ j ][ 0 ] = sum[ i ][ 1 ] - sum[ i ][ 0] = C[ j ][ 1 ] = C[ i ][ 1 ];
sum[ j ][ 2 ] - sum[ j ][ 0 ] = sum[ i ][ 2 ] - sum[ i ][ 0] = C[ j ][ 2 ] = C[ i ][ 2 ];
...
sum[ j ][ K - 1 ] - sum[ j ][ 0 ] = sum[ i ][ K - 1 ] - sum[ i ][ 0] = C[ j ][ K - 1 ] = C[ i ][ K - 1 ];
其中 C[ i ][ 1 ...K-1 ]是sum[ i ][ 1 ... K - 1] - sum[ i ][ 0 ]计算得到。这样得到一个新的矩阵C[N][K]
C[ j ][ 1 ] = C[ i ][ 1 ] 表示满足平衡条件的i,j在矩阵C中的行值相同。行值等于j - i,不包括i行,是i+1行到j行。
三步转化附,不包括i行情况处理,当首行在平衡区间内时,必须有一个i = 0行,以便包含这种情况。
四步转化,对于C[N+1][K]矩阵,找出相同两行间隔最大的情况,计算间隔值。
五步转化,用Hash数据结构
方法是对矩阵C逐行构造Hash表,明确几个概念:i是矩阵C的行下标,C[ i ] 是矩阵C的一维行数组,Hash表的位置下标是key,hash表的值是value,存放的是矩阵C的行下标i。
构造hash表是往hash表插入值的过程。
step1 构造hash函数,函数自变量是矩阵C的一维行数组C[i],函数因变量是hash表的位置下表key。
step2 逐行遍历矩阵C,计算每行key值,如果hash表对应key位置下标值为空,填入行下标i;如果下标值不空,取出key位置hash表值value,得到C[value]一维行数组,将它和当前一维行数组比较,如果两个数组相同,说明满足平衡区间条件,记录区间长度,如果比已知的区间长度maxlen大,更新maxlen;如果两个数组不同,说明是hash表发生冲突,采用线性探测再散列方法,key后移,直到满足条件再插值。
可以看出,整个过程只构造hash表,不查询hash表。
其他
此题用到矩阵运算
hash用起来雷声不断,现在可以明白的是hash构造的key,value概念的转换。继续努力。
算法设计的漏洞
一个是处理冲突key后移时,可能会超过hash表范围,所以要取余计算。
一个是计算区间范围要单独设计i=0情况,否则不能覆盖首行包含在平衡区间的场景。
性能一直不满足要求,分析有两点
一是hash函数选择,二是矩阵运算,生成hash表的方式。
前者看不懂,hash函数网上的例子各种奇怪符号,蒙圈了。
后者的性能问题是feature矩阵,sum矩阵,c矩阵,hash表分成三次循环,实际上可以并为一个循环,feature矩阵直接去掉,sum矩阵生成一行直接用来计算c矩阵。
此题做了3天,个中辛苦一言难尽!
天下事都是人做的,不要怕!
参考代码:https://blog.csdn.net/liu940204/article/details/51254821
代码链接
https://github.com/xierensong/learngit/blob/master/poj/p3274.cpp