zoukankan      html  css  js  c++  java
  • hihocoder1260,1261 (HASH经典题)

    这两题比赛做的时候各种卡,太久没有写过这种类型的题目了。各种细节想不清楚。 赛后看下网上大部分题解的代码,发现大部分都是直接用TRIE+暴力直接搞的--!,随便找了份代码发现有些数据这种做法是超时的,比如n=m=1,然后下面两行长度为100000的全为a的字符串。明显直接暴力DFS复杂度为n*n.

    比赛的时候,还想用26*100000*log(n)然后用STL来去重复,结果直接超时果真STL还是太耗时了。。。

    1260解法:

    因为字符串集合S中N个字符串是两两不同的(这点比较关键,利用这点可以减少代码量),所以对S中得N个字符进行HASH处理(可以只得到HASH值不建HASH表,查询时候用二分)。然后对于需要查询的M个字符串,因为这M个字符串总长是10^5,所以在每个字符串每一位暴力插入(‘a’-‘z’),也就是要枚举26*10^5种情况,用一次预处理可以使得每次HASH操作为O(1),最后对这26*10^5种情况到HASH表里面去找。这里还有一个去重复的小技巧,对于待查询字符串m,如果出现的序号是k,则每次到HASH表去找时,查找到得时候用一个数组保存序号k,下次m再找到到时则忽略。 

    直接上大神的代码:

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    unsigned long long x[11000],key=10007,Key[110000],pre[110000],ne[110000];
    char ch[110000];
    int n,m,pd[11000],sign;
    int find(unsigned long long k){
        int l=1,r=n+1;
        while (l<r){
            int mid=l+r>>1;
            if (x[mid]==k){
                if (pd[mid]!=sign){
                    pd[mid]=sign; return 1;
                } else return 0;
            }
            if (x[mid]>k) r=mid; else l=mid+1;
        }
        return 0;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++){
            scanf("%s",ch+1); int len=strlen(ch+1);
            for (int j=1;j<=len;j++) x[i]=x[i]*key+ch[j];
        }
        Key[0]=1;
        for (int i=1;i<=100000;i++) Key[i]=Key[i-1]*key;
        sort(x+1,x+n+1);
        for (;m;m--){
            scanf("%s",ch+1); int len=strlen(ch+1); int ans=0; sign++;
            for (int j=1;j<=len;j++) pre[j]=pre[j-1]*key+ch[j];
            ne[len+1]=0;
            for (int j=len;j;j--) ne[j]=ne[j+1]+ch[j]*Key[len-j];
            for (int i=0;i<=len;i++)
                for (int j='a';j<='z';j++){
                    unsigned long long now=pre[i]*Key[len-i+1]+j*Key[len-i]+ne[i+1];
                    ans+=find(now);
                }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    再附上我的又乱又长的代码:

    //
    //  main.cpp
    //  hc17
    //
    //  Created by 陈加寿 on 15/12/27.
    //  Copyright (c) 2015年 chenhuan001. All rights reserved.
    //
    
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <map>
    #include <set>
    #include <string>
    #include <algorithm>
    using  namespace std;
    #define K 31
    unsigned long long myhash[10010];
    char str[100100];
    unsigned long long g[100100];
    int n,m;
    
    int tsave[10010];
    int tcnt;
    int tmark[10010];
    
    map<unsigned long long,int> mhash;
    set<unsigned long long> mark;
    
    int myfind(unsigned long long x)
    {
        //找到x在myhash中出现的次数
        int b=1,d=n;
        while(b<d)
        {
            int mid=(b+d)/2;
            if( x <= myhash[mid] )
                d = mid;
            else b = mid+1;
        }
        if(myhash[b]!=x || tmark[b]==1) return 0;
        
        tmark[b]=1;
        tsave[ tcnt++ ]=b;
        
        return 1;
        
        int tb=b;
        b=1,d=n;
        while(b<d)
        {
            int mid=(b+d+1)/2;
            if( x >= myhash[mid] )
                b = mid;
            else d= mid-1;
        }
        return b-tb+1;
    }
    
    
    int main() {
        
        scanf("%d%d",&n,&m);
        
        //mhash.clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str);
            unsigned long len=strlen(str);
            unsigned long long tmp=1;
            unsigned long long sum=0;
            
            for(int j=0;j<len;j++)
            {
                sum += tmp*(str[j]-'a'+1);
                tmp = tmp*K;
            }
            //mhash[sum]++;
            myhash[ i ] = sum;
        }
        
        sort(myhash+1,myhash+n+1);
        memset(tmark,0,sizeof(tmark));
        
        for(int i=0;i<m;i++)
        {
            //mark.clear();
            tcnt=0;
            
            scanf("%s",str);
            int len=strlen(str);
            
            unsigned long long tmp=1;
            for(int j=0;j<len;j++)
            {
                g[j] = tmp*(str[j]-'a'+1);
                tmp = tmp*K;
            }
            
            for(int j=len-2;j>=0;j--)
            {
                g[j] += g[j+1];
            }
            g[len]=0;
            
            int ans=0;
            tmp = 1;
            unsigned long long nw=0;
            
            for(int j=-1;j<len;j++)
            {
                for(int p=1;p<=26;p++)
                {
                    unsigned long long tt=nw+p*tmp+g[j+1]*K;
                    
                    if(myfind(tt)==1)
                    {
                        ans++;
                    }
                    
                }
                
                nw += tmp*(str[j+1]-'a'+1);
                tmp *=K;
            }
            
            for(int j=0;j<tcnt;j++)
                tmark[ tsave[j] ]=0;
            
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    1261解法:

    这题有个十分关键的思路,在比赛的时候没有想到。--! 真是蒟蒻。

    假设有字符串s和字符串t,如果s要恰好添加两个字符变成t,普通的思维至少得要26*len(s)^2次操作才能完成。但是用两边向中心的经典思考方式,于是就可以在s中添加一个字符,从t中删除一个字符。使用HASH复杂度变为len(s)*26。

    同理剩下来两种情况。

    
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define HASHN 5000007
    #define INF 10000000
    
    #define K 31
    typedef unsigned long long ull;
    
    vector<ull>s[10100];
    vector<ull>t[10100];
    
    //不需要二分,不需要排序。
    
    struct HashNode
    {
        int next;
        ull key;//防止冲突
        int id;
    }hashnode[3000000];
    
    int pre[ HASHN ],cnt;
    int ansans[100100][3][3];
    
    void hash_insert(ull key,int id)
    {
        int hashkey = key%HASHN;
        int num=0;
        for(int p=pre[hashkey];p!=-1;p = hashnode[p].next)
        {
            ull nkey = hashnode[p].key ;
            int nid = hashnode[p].id ;
            if( nkey == key )
            {
                if(id==nid) return ;
                num++;
                if(num>=3) return ;//有三个了,直接返回.
            }
        }
        //没有找到相同的,或者相同数目不超过三个,则插入
        hashnode[cnt].key = key;
        hashnode[cnt].id = id;
        hashnode[cnt].next = pre[hashkey];
        pre[ hashkey ] = cnt++;
    }
    
    void update(int s[3],int x)
    {
        if(x==s[0]||x==s[1]||x==s[2]) return ;
        if(x<s[0])
        {
            s[2]=s[1];
            s[1]=s[0];
            s[0]=x;
        }
        else if(x<s[1])
        {
            s[2]=s[1];
            s[1]=x;
        }
        else if(x<s[2]) s[2]=x;
    }
    
    void myfind(ull x,int s[3])
    {
        //找到x在myhash中出现的次数
        int hashkey = x%HASHN;
        for(int p=pre[hashkey];p!=-1;p=hashnode[p].next)
        {
            ull nkey = hashnode[p].key ;
            int nid = hashnode[p].id ;
            if(nkey==x)
            {
                update(s,nid);
            }
        }
        
        return ;
    }
    
    
    int main(int argc, const char * argv[]) {
        int n,m;
        scanf("%d%d",&n,&m);
        char str[100100];
        unsigned long len;
        
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            len=strlen(str);
            for(int j=0;j<len;j++)
                s[i].push_back(str[j]-'a'+1);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%s",str);
            unsigned long len=strlen(str);
            for(int j=0;j<len;j++)
                t[i].push_back(str[j]-'a'+1);
        }
        
        //第一个问题。t添加两个字母,等价于s删除一个,t添加一个
        //第一步,将s进行hash
        cnt=0;
        memset(pre,-1,sizeof(pre));
        ull savetmp[100100];
        for(int i=0;i<n;i++)
        {
            len = s[i].size();
            ull tmp=1;
            for(int j=1;j<len;j++)
            {
                savetmp[j] = tmp*s[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=1;j--)
                savetmp[j] += savetmp[j+1];
            tmp=1;
            ull sum=0;
            for(int j=0;j<len;j++)//删除第j个点
            {
                hash_insert(sum+savetmp[j+1],i);
                sum += s[i][j]*tmp;
                tmp *= K;
            }
        }
        
        int ans[3];
        /*
        for(int i=0;i<nodecnt;i++)
        {
            printf("nodecnt%d : key=%lld id=%d
    ",i,g[i].key,g[i].id);
        }
         */
        
        for(int i=0;i<m;i++)
        {
            len = t[i].size();
            ull tmp=K;
            for(int j=0;j<len;j++)
            {
                savetmp[j] = tmp*t[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=0;j--)
                savetmp[j] += savetmp[j+1];
            
            ans[0]=ans[1]=ans[2]=INF;
            ull sum=0;
            tmp=1;
            for(int j=0;j<=len;j++)
            {
                for(int k=1;k<=26;k++)
                {
                    myfind(sum+k*tmp+savetmp[j],ans);
                }
                sum += tmp*t[i][j];
                tmp *= K;
            }
            
            for(int j=0;j<3;j++)
            {
                if(ans[j]!=INF) ansans[i][0][j]=ans[j]+1;
                else ansans[i][0][j]=-1;
            }
            //printf("
    ");
        }
        
        //修改两个的时候。
        cnt=0;
        memset(pre,-1,sizeof(pre));
        
        for(int i=0;i<n;i++)
        {
            len = s[i].size();
            ull tmp=K;
            for(int j=1;j<len;j++)
            {
                savetmp[j] = tmp*s[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=1;j--)
                savetmp[j] += savetmp[j+1];
            tmp=1;
            ull sum=0;
            for(int j=0;j<len;j++)//修改第j个点
            {
                for(int k=1;k<=26;k++)
                    hash_insert(sum+tmp*k+savetmp[j+1],i);
                sum += s[i][j]*tmp;
                tmp *= K;
            }
        }
        /*
        for(int i=0;i<nodecnt;i++)
        {
            printf("nodecnt%d : key=%lld id=%d
    ",i,g[i].key,g[i].id);
        }
        */
        for(int i=0;i<m;i++)
        {
            len = t[i].size();
            ull tmp=1;
            for(int j=0;j<len;j++)
            {
                savetmp[j] = tmp*t[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=0;j--)
                savetmp[j] += savetmp[j+1];
            
            ans[0]=ans[1]=ans[2]=INF;
            ull sum=0;
            tmp=1;
            for(int j=0;j<len;j++)
            {
                for(int k=1;k<=26;k++)
                {
                    myfind(sum+k*tmp+savetmp[j+1],ans);
                }
                sum += tmp*t[i][j];
                tmp *= K;
            }
            
            for(int j=0;j<3;j++)
            {
                if(ans[j]!=INF) ansans[i][1][j]=ans[j]+1;
                else ansans[i][1][j]=-1;
            }
        }
        
        
        //删除两个
        cnt=0;
        memset(pre,-1,sizeof(pre));
        
        for(int i=0;i<n;i++)
        {
            len = s[i].size();
            ull tmp=K;
            for(int j=0;j<len;j++)
            {
                savetmp[j] = tmp*s[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=0;j--)
                savetmp[j] += savetmp[j+1];
            tmp=1;
            ull sum=0;
            for(int j=0;j<=len;j++)//添加第j个点
            {
                for(int k=1;k<=26;k++)
                    hash_insert(sum+tmp*k+savetmp[j],i);
                sum += s[i][j]*tmp;
                tmp *= K;
            }
        }
        
        /*
         for(int i=0;i<nodecnt;i++)
         {
         printf("nodecnt%d : key=%lld id=%d
    ",i,g[i].key,g[i].id);
         }
         */
        for(int i=0;i<m;i++)
        {
            len = t[i].size();
            ull tmp=1;
            for(int j=1;j<len;j++)
            {
                savetmp[j] = tmp*t[i][j];
                tmp *= K;
            }
            savetmp[len] = 0;
            for(int j=len-1;j>=1;j--)
                savetmp[j] += savetmp[j+1];
            
            ans[0]=ans[1]=ans[2]=INF;
            ull sum=0;
            tmp=1;
            for(int j=0;j<len;j++)
            {
                myfind(sum+savetmp[j+1],ans);
                
                sum += tmp*t[i][j];
                tmp *= K;
            }
            
            for(int j=0;j<3;j++)
            {
                if(ans[j]!=INF) ansans[i][2][j]=ans[j]+1;
                else ansans[i][2][j]=-1;
            }
        }
        
        for(int i=0;i<m;i++)
            for(int j=0;j<3;j++)
            {
                for(int k=0;k<3;k++)
                    printf("%d ",ansans[i][j][k]);
                printf("
    ");
            }
        return 0;
    }
  • 相关阅读:
    一种分布式框架设计(四)
    读书笔记-《拆掉思维里的墙》
    [JS前端开发] js/jquery控制页面动态载入数据 滑动滚动栏自己主动载入事件
    Qt 5.3更新无数,更改C++控制台输出最为赞
    Guava ---- Ordering排序工具
    codeforces 558D Guess Your Way Out! II 规律
    Linux shell之打印输出
    细说Oracle中NULL值
    责任成本汇总表
    NSOperationQueue小结
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/5121710.html
Copyright © 2011-2022 走看看