hash专题

来源:互联网 发布:野草教学设计软件 编辑:程序博客网 时间:2024/06/06 09:21

ural1486二维hash

http://acm.timus.ru/problem.aspx?space=1&num=1486

题意:

一个最大500*500的字符矩阵,求最大的两个相同的字符正方形。正方形可以有重叠部分但不能重合。

tip:

二分答案,二分查找正方形的边长,然后使用一个Hash表来判断该边长是否可行。Hash函数二维情况。时间复杂度是O(NMlog(N*M))的。检查是否产生了相同的Hash函数,如果存在相同的Hash函数,则认为该边长可行,否则即认为它不可行。

#include <cstdio>#include <iostream>#include <cstring>#include <map>using namespace std;typedef unsigned long long ui;typedef long long LL;const int maxn = 510;const ui MOD = 1e5+7;int n,m,ans,ans1x,ans2x,ans1y,ans2y;//n行m列LL cnt;char s[maxn][maxn];ui P = 233,Q= 23333,nc[maxn],mc[maxn],h[maxn][maxn];//P:列上的乘数,Q:行上的乘数void init(){    ans =0;    nc[0] = 1;mc[0] = 1;    for(int i = 1 ;i <= n ; i++){        nc[i] = nc[i-1] * P;        //cout <<"nc"<<nc[i]<<endl;        mc[i] = mc[i-1] * Q;    }    for(int i =  1 ;i <= n ; i++){        scanf("%s",s[i]+1);    }    for(int i = 1; i <= n ; i++){        //cout<<s[i]+1<<endl;        for(int j = 1; j <= m ; j++){            h[i][j] = h[i][j-1]*Q+s[i][j]-'a'+1;//一行的        }    }    for(int i = 2; i <= n ; i++){        for(int j = 1; j <= m ; j++){            h[i][j] = h[i][j]+h[i-1][j]*P;//加上前面行的*列的乘数        }    }//    for(int i =  1; i <= n ; i++){//        for(int j = 1; j <= m ; j++)//            cout <<h[i][j]<<" ";//        cout <<endl;//    }}ui gethash(int x1,int x2,int y1,int y2,int mid){    ui tmp =  h[x2][y2] - h[x1-1][y2]*nc[mid] - h[x2][y1-1]*mc[mid];    tmp += h[x1-1][y1-1]*nc[mid]*mc[mid];    return tmp;}struct node{    LL k;    int x,y;    ui data;}book[100010];bool check(int mid){    cnt++;    for(int i = 1; i <= n-mid+1 ; i++){        for(int j = 1; j <= m-mid+1; j++){            ui _hash = gethash(i,i+mid-1,j,j+mid-1,mid);//            if(mid == 3 && i == 1&& j == 1)//                cout <<"hash1 = "<<_hash<<endl;//            if(mid == 3&& i == 3 && j == 3)//                cout<<"hash2 = "<<_hash<<endl;            ui pos = _hash%MOD;            if(book[pos].k == cnt&& book[pos].data == _hash){                if(mid > ans){                    ans = mid;                    ans1x = book[pos].x;                    ans1y = book[pos].y;                    ans2x = i;                    ans2y = j;                }                return true;            }            book[pos].k=cnt;book[pos].x = i;book[pos].y = j;book[pos].data=_hash;        }    }    return false;}void sov(){    int l = 1 ; int r = min(n,m);    while(l <= r){       int mid = (l+r)/2;       //cout <<"md = "<<mid<<endl;       if(check(mid)){            l = mid+1;       }       else{            r = mid-1;       }    }    if(ans1x > ans2x){        swap(ans1x,ans2x);        swap(ans1y,ans2y);    }    if(ans)        printf("%d\n%d %d\n%d %d\n",ans,ans1x,ans1y,ans2x,ans2y);    else    printf("0\n");}int main(){    while(~scanf("%d%d",&n,&m)){        init();        sov();    }}

vijos 有根树同构,图的hash

题意:

判断两个树同构

tip:

转换成判断两个图是否同构,对每个点的度数做hash,迭代(10层?),初始为每个点最开始的度,迭代时用上一次别的点迭代的结果(所有连边的点度数排个序)和这个点的迭代PQ不设成一个。

图的hash还可以距离啊什么

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>#include <map>using namespace std;const int maxn =110;typedef unsigned long long ui;int a,b,n,T,tot,head[maxn],TAT[maxn],sorthead[maxn],tot2,head2[maxn];ui d[maxn],tmp[maxn],so[maxn],ans[maxn];map<ui,int> mp;ui P = 1e9+7,Q = 1e9+9,X = 3214567;struct node{    int v,next;}edges[maxn],edges2[maxn];void add(int u,int v){    edges[tot].v = v;edges[tot].next = head[u];head[u] = tot++;    edges[tot].v = u;edges[tot].next = head[v];head[v] = tot++;}void addt(int u,int v){    edges[tot].v = v;edges[tot].next = head[u];head[u] = tot++;}void add2(int u,int v){    edges2[tot2].v = v;edges2[tot2].next = head2[u];head2[u] = tot2++;}ui gethash(){    int cl = 10;    while(cl--){        for(int i = 1; i <= n ; i++){            int cnt = 0;            tmp[i] = 0;            for(int k = head[i]; k != -1; k = edges[k].next){                so[cnt++] = d[edges[k].v];            }            sort(so,so+cnt);            for(int j = 0 ; j < cnt ;j++)                tmp[i] = tmp[i]*P+so[j];            tmp[i] = tmp[i]+d[i]*Q;        }        for(int i = 1; i <= n ; i++){            d[i] = tmp[i];        }    }    sort(d+1,d+n+1);    ui no = d[1];    for(int i = 2; i <= n ; i++)        no = no*X+d[i];    return no;}void init(){    scanf("%d%d",&T,&n);    for(int k = 1; k <= T ; k++){        tot= 0;        for(int i = 0; i <= n; i++)            d[i] = 0,head[i] = -1;        for(int i = 1; i < n ; i++){            scanf("%d%d",&a,&b);            d[a]++;d[b]++;            add(a,b);        }        ans[k] = gethash();        //cout << ans[k]<<"we"<<endl;    }}void sov(){    tot = 0;    for(int i = 0 ; i <= T ;i++)        head[i] = -1;    for(int i = 1; i <= T ; i++){        if(mp[ans[i]]== 0)            mp[ans[i]] = i;        addt(mp[ans[i]],i);            //cout << mp[ans[i]]<<" -."<<i<<endl;       // cout <<"mp[ "<<ans[i]<<" ] = "<<mp[ans[i]]<<endl;    }    int cl = 0;    for(int i = 0 ; i <= T ;i++)        head2[i] = -1;    tot2 = 0;    for(int i = 1; i <= T ; i++){        if(mp[ans[i]] == i){            //cout <<" i = "<<i<<endl;            for(int p = 0 ;p <= T ;p++) TAT[p] = 0;            int num = 0;            for(int k = head[i] ; k != -1; k  = edges[k].next){                //cout << "i = "<<i <<" next = "<<edges[k].v<<endl;                TAT[num++] = edges[k].v;            }            sort(TAT,TAT+num);            sorthead[cl++] = TAT[0];            for(int k = num-1; k >= 1 ; k--)                add2(TAT[0],TAT[k]);        }    }    sort(sorthead,sorthead+cl);    for(int i = 0 ; i < cl ; i++){        printf("%d",sorthead[i]);        for(int k = head2[sorthead[i]] ; k != -1; k = edges2[k].next)   printf("=%d",edges2[k].v);         printf("\n");    }}int main(){    init();    sov();}

uva11996

题意:

题意:给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:
1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入
2 p 删除第p个字符,后面的字符往前移
3 p1 p2反转第p1到第p2个字符
4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。

tip:

分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。
1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来
若合格,O(lenth)的复杂度重新算hash
2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)
3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)
4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)

上述复杂度的lenth都是根号n的规模

以下引用论文:
如果两个串不会变化,求LCP只需要求出A+B的后缀数组即可。但是本题的A串是不断变化的,而且由变化的方式可以看出,每次操作都会导致后缀数组发生很大的变化,因此我们应该另辟蹊径。
对于一个k,不难判断两个串的LCP是否有至少k个字符:计算这两个串的前k个字符的Hash值,并且比较它们是否相等。如果相等,就几乎可以肯定地认为这两个串的LCP至少有k个字符,否则,它们的LCP长度肯定不到k。这样,就可以通过二分查找来计算LCP。
现在问题又转化成了怎么求一个子串的Hash。不妨仍然采用Rabin-Karp的Hash函数,如果已知了S1和S2的Hash值,不难求出S1+S2的Hash值:

而 的Hash值显然又是可以在O(n)时间 内预处理的得到的。因此,可以在O(1)时间内通过两个字符串的Hash值得到它们连接后得到的串的Hash值。因此,可以使用块状链表维护计算A中子串的Hash值,方法于维护计算部分和类似,不同之处在于一个字符串正向和反向的Hash值是不同的,为了能在O(n0.5)时间内完成reverse操作,应当要能在O(1)时间内把一个块“反转”,这就要求我们为一个块维护两个Hash值:一个是正向的,一个是倒向的。除此之外的操作于维护部分和或者维护最大值类似。这样,插入,删除,反转操作是O(n0.5)的,而查询操作是O(n0.5logn)的。
类似地,本题的另一种解法是在一棵splay树上维护Hash值。每次一个节点被旋转或以它为根的子树被修改时,则计算它的正向Hash值和反向Hash值,这样,就可以在O(1)时间你reverse一棵子树,通过split可以不难地把一棵子树在均摊O(logn)时间内反转。插入,删除操作显然也是均摊O(logn)的,而查询操作的均摊时间复杂度为O(log2n)

/*题意:给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入2 p   删除第p个字符,后面的字符往前移3 p1 p2反转第p1到第p2个字符4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。*//*分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来若合格,O(lenth)的复杂度重新算hash2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)上述复杂度的lenth都是根号n的规模*/#include <cstdio>#include <iostream>#include <cmath>#include <cstring>using namespace std;const int maxn = 3000;const int maxm = 4e6+10;typedef unsigned long long ui;ui P = 3,P_power[maxn];struct node{    int index,left,right,lenth;    int is_r;    ui f_hash,b_hash;    char s[maxn];     void roll(){        if(is_r == 1){            is_r = 0;            for(int i = 0 ,j = lenth-1; i <= j; i++,j--)                swap(s[i],s[j]);        }    }    void print(){        //roll();        if(is_r == 0)        for(int i = 0; i < lenth ; i++) cout <<s[i];        else            for(int i = lenth-1; i >= 0 ; i--)  cout <<s[i];        cout <<endl;        printf("block[%d].left = %d  right = % d  lenth = %d  f_hash = %d  b_hash = %d\n",index,left,right,lenth,f_hash,b_hash);    }    ui getb_hash(){        ui tmp = 0;        for(int i = lenth-1 ;i >= 0 ; i--)            tmp = tmp *P+s[i]-'0'+1;        return tmp;    }    ui getf_hash(){        ui tmp = 0;        for(int i = 0 ;i <lenth ; i++)            tmp = tmp *P+s[i]-'0'+1;        return tmp;    }}block[maxn];int n,m,low_size,up_size,num_block,cp,pos,num,p1,p2,tot;char ini[maxm],c[maxn],d[maxn];void init(){    scanf("%s",ini);    up_size = sqrt(n+m);low_size = up_size/2;    //printf("up _ size = %d   low = %d\n",up_size,low_size);    block[0].right = 1;num_block = n/low_size;    int last_size = n-num_block*low_size+low_size;    int pos = 0;    //1~倒数第二块    for(int i = 1; i < num_block ; i++){        ui tmp = 0,bk = 0;        block[i].lenth = low_size;block[i].left = i-1;block[i].right = i+1;block[i].index = i;        for(int j = 0 ;pos < n && j < block[i].lenth;j++, pos++){            block[i].s[j] = ini[pos];            tmp = tmp*P+ini[pos]-'0'+1;        }        for(int j = block[i].lenth-1; j >= 0; j--)            bk = bk*P+block[i].s[j]-'0'+1;        block[i].f_hash = tmp;block[i].b_hash = bk;        //cout <<"hahahah  "<<block[i].b_hash<<endl;        block[i].is_r = 0;    }    //最后一块    block[num_block].left = num_block-1;block[num_block].right = maxn-1;block[maxn-1].left = num_block;block[maxn-1].index = maxn-1;    block[num_block].lenth = last_size;block[num_block].index = num_block;block[num_block].is_r = 0;    for(int j = 0;pos < n ;j++,pos++){       block[num_block].s[j] = ini[pos];    }    block[num_block].f_hash = block[num_block].f_hash;block[num_block].b_hash = block[num_block].b_hash;}void separate(int s_wi){    ++num_block;    int l1 = block[s_wi].lenth/2;    int l2 = block[s_wi].lenth-l1;    block[num_block].is_r = 0;    for(int i = l1 ,j = 0; i < block[s_wi].lenth ; i++,j++)   block[num_block].s[j] = block[s_wi].s[i];    block[num_block].index = num_block;    block[num_block].left = s_wi;block[num_block].right = block[s_wi].right;block[num_block].lenth = l2;    block[block[s_wi].right].left = num_block;block[s_wi].right = num_block;block[s_wi].lenth = l1;    int c1 = s_wi,c2 = num_block;    block[c1].f_hash = block[c1].getf_hash();    block[c2].f_hash = block[c2].getf_hash();    block[c1].b_hash = block[c1].getb_hash();    block[c2].b_hash = block[c2].getb_hash();}void cp1(int pos,int add){    if(pos == 0){        block[block[0].right].roll();        for(int i = block[block[0].right].lenth; i >= 1 ; i--)            block[block[0].right].s[i] = block[block[0].right].s[i-1];        block[block[0].right].s[0] = add+'0';        block[block[0].right].lenth++;        int c1 = block[0].right;        block[c1].f_hash = block[c1].getf_hash();        if(block[block[0].right].lenth > up_size){            separate(block[0].right);        }        return;    }    int i,cl = block[0].right;    while(pos > block[cl].lenth){        pos-=block[cl].lenth;        cl = block[cl].right;    }    block[cl].roll();    block[cl].lenth++;    for(int i = block[cl].lenth; i > pos ; i--)        block[cl].s[i] = block[cl].s[i-1];    block[cl].s[pos] = add+'0';    block[cl].f_hash = block[cl].getf_hash();    block[cl].b_hash = block[cl].getb_hash();    if(block[cl].lenth > up_size)   separate(cl);}ui get_hash(int len,int p1){    int c1 = block[0].right;    int c3,p3 = len+p1-1;    while(p1 > block[c1].lenth){        p1 -= block[c1].lenth;        p3 -= block[c1].lenth;        c1 = block[c1].right;    }    block[c1].roll();    p1--;//c1块的第p1位置(下标)开始    c3 = c1;    while(p3 > block[c3].lenth){        p3 -= block[c3].lenth;        c3 = block[c3].right;    }    block[c3].roll();    ui tmp = 0,tp = 0;    if(c1 == c3){//同一个快        for(int i = p1 ; i < p3; i++)            tmp = tmp*P+block[c1].s[i]-'0'+1;    }    else{        for(int i = p1 ;i < block[c1].lenth; i++)   tmp = tmp*P+block[c1].s[i]-'0'+1;        for(int i = c1 ; i != block[c3].left ;i = block[i].right)   tmp = tmp*P_power[block[block[i].right].lenth]+block[block[i].right].f_hash;        for(int i = 0 ; i < p3; i++)   tp = tp*P+block[c3].s[i]-'0'+1;        tmp = tmp*P_power[p3]+tp;    }    return tmp;}bool se_check(int len,int p1,int p2){    ui k1 = get_hash(len,p1);    ui k2 = get_hash(len,p2);    if(k1 == k2)    return true;    else    return false;}int cp4(int p1,int p2){    int l = 1, r = tot - p2+1,ans = 0;    while(l <= r){        int mid = (l+r)/2;        if(se_check(mid,p1,p2)){            ans = max(ans,mid);            l = mid+1;        }        else    r = mid-1;    }    return ans;}void check(){    for(int i = block[0].right ; i != maxn-1 ; i= block[i].right){        block[i].print();    }}void _union(int cl){    int oth = block[cl].right;    block[block[oth].right].left = cl;    block[cl].right = block[oth].right;    for(int i = block[cl].lenth ,j = 0; j < block[oth].lenth ;i++,j++)        block[cl].s[i] = block[oth].s[j];    block[cl].lenth = block[cl].lenth+block[oth].lenth;    block[cl].f_hash = block[cl].getf_hash();    block[cl].b_hash = block[cl].getb_hash();    if(block[cl].lenth > up_size)   separate(cl);}void cp2(int pos){    int cl = block[0].right;    while(pos > block[cl].lenth){        pos -= block[cl].lenth;        cl = block[cl].right;    }    block[cl].roll();    for(int i = pos-1 ;i < block[cl].lenth-1 ; i++){        block[cl].s[i] = block[cl].s[i+1];    }    block[cl].lenth--;    block[cl].f_hash = block[cl].getf_hash();    block[cl].b_hash = block[cl].getb_hash();    if( block[cl].lenth < low_size)  {        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;        if(block[cl].right == maxn-1 ){            block[block[cl].left].roll();            _union(block[cl].left);        }        else {            block[block[cl].right].roll();            _union(cl);        }    }}void cp3(int p1,int p2){    //check();    int c1 = block[0].right;    while(p1 > block[c1].lenth){        p1 -= block[c1].lenth;        c1 = block[c1].right;    }    block[c1].roll();    //block[c1].print();    int c2 = block[0].right;    while(p2 > block[c2].lenth){        p2 -= block[c2].lenth;        c2 = block[c2].right;    }    block[c2].roll();   // block[c2].print();    if(c1 == c2){        for(int i = p1-1 ,j = p2-1; i <= j ; i++,j--){            swap(block[c1].s[i],block[c1].s[j]);        }        block[c1].f_hash = block[c1].getf_hash();        block[c1].b_hash = block[c1].getb_hash();        return;    }    for(int i = block[c1].right ; i != c2; ){        int j= block[i].right;        swap(block[i].right,block[i].left);        swap(block[i].f_hash,block[i].b_hash);        block[i].is_r ^= 1;        i = j;       // cout <"in";    }    if(block[c1].right != c2){        swap(block[c1].right , block[c2].left);        block[block[c2].left].right = c2;        block[block[c1].right].left = c1;    }    int lenc = 0,lend = 0;    for(int i = 0 ; i < p1-1;i++)        c[lenc++] = block[c1].s[i];    for(int i = p2-1 ; i >= 0 ; i--)        c[lenc++] = block[c2].s[i];    for(int i = block[c1].lenth-1; i >= p1-1 ; i--)        d[lend++] = block[c1].s[i];    for(int i = p2; i < block[c2].lenth ; i++)        d[lend++] = block[c2].s[i];    for(int i = 0 ; i < lenc ; i++){        //cout <<c[i];        block[c1].s[i] = c[i];    }   // cout <<endl;    for(int i = 0 ; i < lend ; i++){        block[c2].s[i] = d[i];       // cout <<d[i];    }   // cout <<endl;    block[c1].lenth = lenc;block[c2].lenth = lend;    block[c1].f_hash = block[c1].getf_hash();    block[c2].f_hash = block[c2].getf_hash();    block[c1].b_hash = block[c1].getb_hash();    block[c2].b_hash = block[c2].getb_hash();   // cout <<"len1 = "<<lenc <<"  lenb = "<<lend<<endl;    if(lenc > up_size)  separate(c1);    if(lend > up_size)  separate(c2);    if(lenc < low_size){        int cl = c1;        //block[cl].print();        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;        if(block[cl].right == maxn-1 ){            block[block[cl].left].roll();            _union(block[cl].left);        }        else {            block[block[cl].right].roll();            _union(cl);        }    }    if(lend <  low_size){        int cl = c2;        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;        if(block[cl].right == maxn-1 ){            block[block[cl].left].roll();            _union(block[cl].left);        }        else {            block[block[cl].right].roll();            _union(cl);        }    }}void sov(){    tot = n;    for(int ca = 1; ca <= m ; ca++){        scanf("%d",&cp);        if(cp == 1){            tot++;            scanf("%d%d",&pos,&num);            cp1(pos,num);            //check();        }        if(cp == 2){            tot--;            scanf("%d",&pos);            cp2(pos);            //check();        }        if(cp == 3){            scanf("%d%d",&p1,&p2);            cp3(p1,p2);            //block[2].print();            //check();        }        if(cp == 4){            scanf("%d%d",&p1,&p2);           //    check();            printf("%d\n",cp4(p1,p2));        }    }}int main(){    freopen("in.txt","r",stdin);    freopen("out.txt","w",stdout);    P_power[0] = 1;    for(int i = 1; i < maxn-1 ; i++)        P_power[i] = P_power[i-1]*P;    scanf("%d%d",&n,&m);    init();    sov();}/*12 1000001000011001 0 14 2 41 1 14 3 41 1 04 2 81 3 04 3 51 7 14 6 91 8 04 1 82 84 6 92 74 3 52 34 2 82 14 3 43 2 93 1 34 1 73 1 24 1 8*/
原创粉丝点击