zoukankan      html  css  js  c++  java
  • 【暖*墟】#Hash# 字符串Hash与例题

    【字符串Hash】

    1.特征与理解

    用于寻找字符组出现的位置次数的问题,即【字符串匹配问题】。

    2.滚动哈希的优化技巧

    选取两个合适的互质数b,h(b<h),假设字符串C=c1c2...cm,

    定义哈希函数为:H(C)=(c1*b^(m-1)+c2*b^(m-2)+....+cm*b^0) mod h

    b为基数,H(C)的处理相当于把字符串看成b进制数。

    这一过程通过递归计算:H(C,k)=H(C,k-1)*b+ck

    判断某段字符与另一匹配串是否匹配,即判断:

    (某段字符:从位置k+1开始的长度为n的子串C'=ck+1ck+2....ck+n)

    H(C')=H(C,k+n)-H(C,k)*b^n 与 H(S) 的关系。

    ---> 预处理字符串所有前缀Hash值。

    ---> 在O(1)时间内查询它的任意子串Hash值。

    对unsigned long long的使用:

    可以用于取模2^64。遇到这种限制条件时就要想到用unsigned long long类型。

    可以简洁地声明为typedef unsigned long long ull。因为ull的范围是[0,2^64-1]。

    ( 2^64:18446744073709551616,即10^19。)

    所以,如果ull类型的整数溢出了,就相当于取模2^64了。

    而long long的范围是[-2^63,2^63-1],因为有符号的第63位表示“正负”而不表示数值

    3.例题与运用

    (1) poj3461 Oulipo

    //qwq
    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    using namespace std;
    typedef unsigned long long ull;
    
    /*【Oulipo】poj3461
    给你两个字符串s1和s2,求出s1在s2中出现的次数。 */
     
    //字符串Hash的模板题
    
    const int b=13331; //base,基数
    
    ull power[1000009]; //存储b^n
    void init(){
        power[0]=1; //b^0=1
        for(int i=1;i<1000000;i++)
            power[i]=power[i-1]*b; //对ull取模
    }
    
    char s1[10009],s2[1000009];
    ull sum[1000009]; //记录主串哈希值的数组
    
    int main(){
        init(); int T; cin>>T;
        while(T--){
            scanf("%s%s",s1+1,s2+1); //从第一位开始输入
            int n=strlen(s1+1),m=strlen(s2+1);
            sum[0]=0; //记录主串哈希值的数组
            for(int i=1;i<=m;i++) //计算主串的滚动Hash值
                sum[i]=sum[i-1]*b+(ull)(s2[i]-'A'+1);
            ull s=0;
            for(int i=1;i<=n;i++) //计算匹配串的Hash值
                s=s*b+(ull)(s1[i]-'A'+1); 
            int ans=0;
            for(int i=0;i<=m-n;i++)
                if(s==sum[i+n]-sum[i]*power[n]) ans++;
            printf("%d
    ",ans);
        }
        return 0;
    }

    (2)poj2406 Power Strings

    //qwq
    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    
    /*【power strings】poj2406
    给出一个不超过1e6的字符串,求这个字符串最多有多少个周期。 */
     
    //字符串Hash的模板题
    
    const int b=131; //base,基数
    const int mod=10009; //h,要mod的数
    int len;
    ull hashs[1001000];
    
    ull cal(int x,ull y){ //y^x
        ull now=1;
        while(x){
            if(x&1) now=(now*y)%mod;
            x>>=1; y=(y*y)%mod;
        }
        return now;
    }
    
    bool check(int x){ //x:最小的循环长度
        ll cc=cal(x,(ull)b);
        for(int i=(x<<1);i<=len;i+=x)
            if((hashs[i]-(hashs[i-x]*cc)%mod+mod)%mod!=hashs[x]) 
            //求H(C,k+n)-H(C,k)*b^n,之后的每一段长度的hash值是否匹配
            //加上mod,防止相减为负值
                return false;
        return true;
    }
    
    int main(){
        while(1){
            char s[1001000];
            scanf("%s",s+1);
            len=strlen(s+1);
            if(len==1&&s[1]=='.') return 0;
            for(int i=1;i<=len;i++) //求出Hash值
                hashs[i]=(hashs[i-1]*b+s[i])%mod;
            for(int i=1;i<=len;i++) //枚举循环的长度
            //↓↓↓最小的循环长度对应最大的周期,即第一个满足的就是最大周期数
                if(len%i==0 && check(i)){ 
                    printf("%d
    ",len/i); break; 
                }
        }
        return 0;
    }

    (3)poj2752

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    
    /*【既是前缀又是后缀的子串】POJ 2752 //kmp
    从大到小输出所有既是前缀又是后缀的字符串的长度。  */
    
    //该前缀串的最后一个字符肯定与s的最后一个字符相同。
    //从n - 1位既最后一位开始回滚,若s[next[n-1]] == s[n-1],
    //则子串s[0,1,2,...,next[n-1]]是满足条件的子串。
    //然后判断s[next[next[n-1]]] == s[n-1]是否成立,
    //这样一直回滚,直到next[next[.....next[n-1]]] == -1为止。
     
    int Next[400005],ans[400005];
    char str[400005];
    int cnt,len;
     
    void getNext(){
        Next[0] = -1; //起点
        int i = 0, j = -1;
        while (i < len){ //设置终点
            if (j == -1 || str[i] == str[j]){ 
            //↑↑↑找不到任何之前的匹配,但这一位可以匹配
                i++; j++; Next[i]=j; //给next赋值
            }
            else j = Next[j]; //回溯到上一个next寻找匹配
        }
    }
     
    int main(){
        while (scanf("%s", str) != EOF){
            len = strlen(str);
            getNext(); //预处理建立next数组
            cnt = 0; //存答案的种类数
            int j = Next[len - 1]; //用next不断向前滚动
            while (j != -1){ 
                if (str[j] == str[len - 1]) 
                    ans[cnt++] = j + 1; //匹配串长度
                j = Next[j];
            }
            for (int i = cnt - 1; i >= 0; --i)
                printf("%d ", ans[i]); //从ans[0]开始记录的
            printf("%d
    ", len);
        }
        return 0;
    }
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    typedef unsigned long long UL;  //hash
    
    UL hashs[400005],mul[400005],K=31,P=1000000031;
    char s[400005];
    
    int main(){
        mul[0]=1;
        for(int i=1;i<=400000;i++) //预处理k^n
            mul[i]=mul[i-1]*K%P;
        while(~scanf("%s", s+1)){
            int n=strlen(s+1);
            for(int i=1;i<=n;i++) //求hash前缀和
                hashs[i]=(hashs[i-1]*K+s[i]-'a'+1)%P;
            for(int i=1;i<=n;i++)
                if(hashs[i]==((hashs[n]-(hashs[n-i]*mul[i]%P))+P)%P) //前缀=后缀
                    printf("%d ", i);
            putchar('
    ');
        }
        return 0;
    }

     

    (4)bzoj3916 friends

    //qwq
    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <map>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    
    /*(bzoj3916)【friends】【hash】
    有三个好朋友喜欢在一起玩游戏,A君写下一个字符串S,
    B君将其复制一遍得到T,C君在T的任意位置(包括首尾)插入一个字符得到U.
    现在你得到了U,请你找出S.  */
    
    const int P=131;
    const int N=2000010;
    ull h[N],t[N];
    map<ull,int>q;
    char ss[N];
    
    int main(){
        int n,ans,len; scanf("%d",&n);
        if(n%2==0||n==1){ puts("NOT POSSIBLE"); return 0; }
        len=(n-1)/2; scanf("%s",ss+1);
        t[0]=1;h[0]=0;
        for(int i=1;i<=n;i++) t[i]=t[i-1]*P; //预处理P^n
        for(int i=1;i<=n;i++) h[i]=h[i-1]*P+(ull)ss[i]; //hash前缀和
        for(int i=1;i<=n;i++){
            if(i<=len){
                ull l=h[i-1],mid=h[len+1]-t[len+1-i]*h[i],r=h[n]-h[n-len]*t[len];
                l=l*t[len-i+1]+mid;
                if(l==r){
                    if(q[l]||(!ans)) ans=i,q[l]=1;
                    else{ puts("NOT UNIQUE"); return 0; }
                }
            }
            if(i==len+1){
                ull l=h[i-1],r=h[n]-t[len]*h[i];
                if (l==r){
                    if(q[l]||(!ans)) ans=i,q[l]=1;
                    else{ puts("NOT UNIQUE"); return 0; }
                } 
            }
            if(i>len+1){
                ull l=h[len],mid=h[i-1]-h[len]*t[i-1-len],r=h[n]-h[i]*t[n-i];
                r=mid*t[n-i]+r; 
                if(l==r){
                    if(q[l]||(!ans)) ans=i,q[l]=1;
                    else{ puts("NOT UNIQUE"); return 0; }
                } 
            }
        }
        if(!ans){ puts("NOT POSSIBLE"); return 0; }
        else{ 
            int t=1;
            while(len--){
                if(t==ans) t=t+1;
                printf("%c",ss[t]);
                t++;
            }
        }
        return 0;
    }
  • 相关阅读:
    PAT 甲级 1115 Counting Nodes in a BST (30 分)
    PAT 甲级 1114 Family Property (25 分)
    PAT 甲级 1114 Family Property (25 分)
    Python Ethical Hacking
    Python Ethical Hacking
    Python Ethical Hacking
    Python Ethical Hacking
    Python Ethical Hacking
    Python Ethical Hacking
    Python Ethical Hacking
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/9334911.html
Copyright © 2011-2022 走看看