Zju 1262 Word [HASH]解题报告
http://andyzh1314.ycool.com/post.1050667.html
Zju 1262 Word [HASH]解题报告
By AndyZhau
单看此题是一个字符串处理的题目,其实不然。因为题目中只有a和b,换种思路来讲,二进制中也只有0和1,并且串的长度最多为15。因此,此题第一个着眼点就是将串转化成数。为了方便后面的排序,定义0代表a,1代表b。
接下来,面对题目中过大的S Solver们不免有些发愁,单纯的枚举超时无疑。这个时候Hash结构派上了用场。因为状态的数目最多也不过2^15。
Hash表我们可以开下,但是由于每次都要对Hash表清空,而题目的上千组数据严重的影响了时间效率。有两种解决办法:
办法1:开散列表。(模函数法)
由于开散列表只需对每个桶进行清空,所以缩小了范围,减少了时间。
办法2:两个索引数组。
第一个索引数组为Ans[],Ans [i] 表示执行了i不时的结果。
第二个索引数组为Pos[],Pos [t] 表示结果为t的伪执行次数。所谓伪执行次数就是说用Pos [t] - Startlen 才是真正的执行次数。这里Startlen是对len的记录,而len是一个全局变量,每执行一次操作len++。而判断Pos [t]和不合法的方法就是判断Pos [t] 是否Startlen 大。
具体一些,假设在第i次运算之后取得了答案t。那么插入操作为:
Ans [i] = t; // 记录状态
Pos [t] = len ++;
如果找到了一个已经出现的状态t,在第i次重复出现了,那么就已经能够确定答案了:
if ( Pos [t] >= Startlen ) // 重复出现
因为产生了一个状态的环,所以答案就是第(S - i )% ( i – Pos [t] + Startlen ) + Pos [t] – Startlen 个Ans[]。
最后,题目要求输出字典序最小的,那么对答案做一次循环枚举就可以了。
另外,也可以对先将每次产生的状态都转化为循环后的最小状态然后再插入Hash表,这样可以再提高一些效率。
代码1:开散列表。
#include <stdio.h>
#include <string.h>
const int Limit = 100000;
const int Hash = 88889;
int N;
int S;
int Start;
int Total;
int pre [8];
char data [17];
int power2 [17];
int Sum = 0;
int len;
struct node {
int Present , Pos , Next;
} Data [Limit];
int Bucket [Hash];
int ord ( int N )
{
int i;
int ret = 0;
for ( i = 0; i < N; i ++ ) {
ret <<= 1;
if ( data [i] == 'b' ) ret ++;
}
return ret;
}
bool init ()
{
if ( scanf ( "%d" , &N ) == EOF ) return false;
int i , t;
scanf ( "%s" , data );
Start = ord ( N );
Total = 0;
for ( i = 0; i < 8; i ++ ) {
scanf ( "%s" , data );
t = ord ( 3 );
if ( data [3] == 'b' ) pre [t] = 1;
else pre [t] = 0;
Total += pre [t];
}
scanf ( "%d" , &S );
return true;
}
void prepare ()
{
int i;
power2 [0] = 1;
for ( i = 1; i < 17; i ++ ) power2 [i] = power2 [i - 1] << 1;
}
void print ( int N , int t )
{
int i;
int smallest = t;
for ( i = 0; i < N; i ++ ) {
if ( t & power2 [N - 1] ) t = (( t ^ power2 [N - 1] ) << 1 ) + 1;
else t <<= 1;
if ( t < smallest ) smallest = t;
}
for ( i = N - 1; i >= 0; i -- )
if ( smallest & power2 [i] ) printf ( "b" );
else printf ( "a" );
printf ( ""n" );
}
int find ( int x , int p )
{
int k;
for ( k = Bucket [x % Hash]; k != -1; k = Data [k].Next )
if ( Data [k].Present == x ) return k;
k = x % Hash;
Data [len].Next = Bucket [k];
Data [len].Present = x;
Data [len].Pos = p;
Bucket [k] = len ++;
return -1;
}
int change ( int s )
{
int i , t;
int ret = 0;
for ( i = 0; i < N; i ++ ) {
t = 0;
if ( s & power2 [i] ) t += 1;
if ( s & power2 [( i + 1 ) % N] ) t += 2;
if ( s & power2 [( i + 3 ) % N] ) t += 4;
if ( pre [t] ) ret = ret | power2 [( i + 1 ) % N];
}
return ret;
}
int Work ()
{
Sum += S;
if ( Total == 0 ) {
if ( S > 1 ) return 0; else return S;
}
if ( Total == 8 ) {
if ( S > 1 ) return power2 [N] - 1; else return S;
}
int t = Start , b , i , j , e , f;
int ddd [30];
for ( i = 0; i < 30; i ++ ) ddd [i] = i % N;
len = 0;
memset ( Bucket , 0xff , sizeof ( Bucket ));
for ( i = 0; i < S; i ++ ) {
f = find ( t , i );
if ( f != -1 ) {
return Data [Data [f].Pos + ( S - i ) % ( i - Data [f].Pos )].Present;
}
e = 0;
for ( j = 0; j < N; j ++ ) {
b = 0;
if ( t & power2 [ddd [j + 1]] ) b += 2;
if ( t & power2 [j] ) b += 1;
if ( t & power2 [ddd [j + 3]] ) b += 4;
if ( pre [b] ) e = e | power2 [ddd [j + 1]];
}
t = e;
}
return t;
}
main ()
{
freopen ( "p.in" , "r" , stdin );
freopen ( "p.out" , "w" , stdout );
prepare ();
while ( init () ) print ( N , Work () );
}
代码2:索引数组。
#include <stdio.h>
#include <string.h>
const int power2 [16] = { 1 , 2 , 4 , 8 , 16 ,
32 , 64 , 128 , 256 , 512 ,
1024 , 2048 , 4096 , 8192 , 16384 , 32768 };
int N , S;
int Start , Total;
int pre [8];
char data [17];
int len = 0;
int Pos [1 << 15];
int Ans [1 << 15];
int ord ( int N )
{
int ret = 0;
for ( int i = 0; i < N; i ++ ) {
ret <<= 1;
if ( data [i] == 'b' ) ret ++;
}
return ret;
}
bool init ()
{
if ( scanf ( "%d" , &N ) == EOF ) return false;
int i , t;
scanf ( "%s" , data );
Start = ord ( N );
Total = 0;
for ( i = 0; i < 8; i ++ ) {
scanf ( "%s" , data );
t = ord ( 3 );
if ( data [3] == 'b' ) pre [t] = 1;
else pre [t] = 0;
Total += pre [t];
}
scanf ( "%d" , &S );
return true;
}
void print ( int t )
{
int i;
for ( i = N - 1; i >= 0; i -- )
if ( t & power2 [i] ) printf ( "b" );
else printf ( "a" );
printf ( ""n" );
}
int Smallest ( int t )
{
int i , ret = t;
for ( i = 1; i < N; i ++ ) {
if ( t & power2 [N - 1] ) t = (( t ^ power2 [N - 1] ) << 1 ) + 1;
else t <<= 1;
if ( t < ret ) ret = t;
}
return ret;
}
int Work ()
{
if ( Total == 0 ) {
if ( S > 1 ) return 0; else return S;
}
if ( Total == 8 ) {
if ( S > 1 ) return power2 [N] - 1; else return S;
}
int i , Num [18];
for ( i = 0; i < N + 3; i ++ ) Num [i] = i % N;
int j , Startlen = len , t = Smallest ( Start ) , Next , PreIndex;
for ( i = 0; i < S; i ++ ) {
if ( Pos [t] >= Startlen )
return Ans [( S - i ) % ( i - Pos [t] + Startlen ) + Pos [t] - Startlen];
Ans [i] = t; Pos [t] = len ++;
Next = 0;
for ( j = 0; j < N; j ++ ) {
PreIndex = 0;
if ( t & power2 [Num [j]] ) PreIndex = 1;
if ( t & power2 [Num [j + 1]] ) PreIndex += 2;
if ( t & power2 [Num [j + 3]] ) PreIndex += 4;
if ( pre [PreIndex] ) Next = Next | power2 [Num [j + 1]];
}
t = Smallest ( Next );
}
return t;
}
main ()
{
memset ( Pos , 0xff , sizeof ( Pos ));
while ( init () ) print ( Work () );
}