• (模板)字符串Hash算法


    题目链接:https://www.luogu.com.cn/problem/P3370

    题意:给n个字符串,求不同的字符串有多少。(n<=1e4,字符串长度<=1500)

    思路:

      字符串模板。

      acm中广泛使用的一种字符串Hash算法“BKDR Hash”,主要思路是选择合适的进制(base应大于字符串元素的最大值,我取得131,据说base太小了会被卡。。,可以比较大的base,像13131、233333,但我试了下这题,得分不如base=131。。不懂,有时候31的base得分比131高),把字符串上的每个字符看成大数上的一位数字,在取模的前提下计算其转换为十进制的结果,代表字符串的Hash值,但可能发生碰撞(不同字符串的Hash值相同)根据不同的方法碰撞的可能性不同,好的Hash算法应保证碰撞的可能性尽可能小。下面列出4种Hash做法:自然溢出(推荐)、单Hash、双Hash、模1e18级别的质数(推荐),详见代码。

    AC code:  

      1. 自然溢出(100分,推荐)。

    /*
     * 自然溢出,用ull,溢出时自动对2^64取模
     * 可以卡,方法见BZOJ 3097 Hash Killer I
    */
    
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef unsigned long long ull;
    ull base=131;
    ull a[10005];
    char s[10005];
    int n,ans;
    
    ull gethash(char s[]){
        int len=strlen(s);
        ull ret=0;
        for(int i=0;i<len;++i)
            ret=ret*base+(ull)s[i];
        return ret;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            a[i]=gethash(s);
        }
        sort(a+1,a+n+1);
        ans=1;
        for(int i=2;i<=n;++i)
            if(a[i]!=a[i-1])
                ++ans;
        printf("%d
    ",ans);
        return 0;
    }

      2. 单hash(80分)

    /*
     * 单hash,不建议
     * 根据生日悖论找上sqrt(MOD)个字符串时
     * 有很大概率碰撞
     * 参见BZOJ 3098 Hash Killer II
     */
    
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef unsigned long long ull;
    ull base=131;
    ull a[10005];
    char s[10005];
    int n,ans;
    ull MOD=19260817;
    
    ull gethash(char s[]){
        int len=strlen(s);
        ull ret=0;
        for(int i=0;i<len;++i)
            ret=(ret*base+(ull)s[i])%MOD;
        return ret;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            a[i]=gethash(s);
        }
        sort(a+1,a+n+1);
        ans=1;
        for(int i=2;i<=n;++i)
            if(a[i]!=a[i-1])
                ++ans;
        printf("%d
    ",ans);
        return 0;
    }

      3. 双Hash(100分)

    /*
     * 双hash,常数大
     * 模两个1e9级别的质数
     * 仅模两个质数的结果都想等时才相等
     * 除了卡时间之外,目前没有办法卡这种做法
     * 见BZOJ 3099 Hash Killer III
     */
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef unsigned long long ull;
    const ull MOD1=19260817;
    const ull MOD2=19660813;
    ull base=131;
    char s[10005];
    int n,ans;
    
    struct node{
        ull x,y;
    }a[10005];
    
    bool cmp(node a,node b){
        return a.x<b.x;
    }
    
    ull gethash(char s[],ull MOD){
        int len=strlen(s);
        ull ret=0;
        for(int i=0;i<len;++i)
            ret=(ret*base+(ull)s[i])%MOD;
        return ret;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            a[i].x=gethash(s,MOD1);
            a[i].y=gethash(s,MOD2);
        }
        sort(a+1,a+n+1,cmp);
        ans=1;
        for(int i=2;i<=n;++i)
            if(a[i].x!=a[i-1].x||a[i].y!=a[i-1].y)
                ++ans;
        printf("%d
    ",ans);
        return 0;
    }

      4. 模1e18级别的质数(100分,推荐做法

    /*
     * 模1e18级别的质数
     * 推荐
     * 既难以被卡,且常数小
     */
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef unsigned long long ull;
    const ull MOD=212370440130137957ll;//1e18位的质数
    ull base=131;
    ull a[10005];
    char s[10005];
    int n,ans;
    
    ull gethash(char s[]){
        int len=strlen(s);
        ull ret=0;
        for(int i=0;i<len;++i)
            ret=(ret*base+(ull)s[i])%MOD;
        return ret;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            a[i]=gethash(s);
        }
        sort(a+1,a+n+1);
        ans=1;
        for(int i=2;i<=n;++i)
            if(a[i]!=a[i-1])
                ++ans;
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    python_网络编程struct模块解决黏包问题
    python_网络编程socket(UDP)
    python_网络编程socket(TCP)
    python_面向对象
    python_生成器
    python_迭代器
    linux实操_shell自定义函数
    linux实操_shell系统函数
    linux实操_shell读取控制台输入
    scrapy-redis 0.6.8 配置信息
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/12401300.html
Copyright © 2020-2023  润新知