( 字符串专题 )【 字符串hash 】

( 字符串专题 )【 字符串hash 】

字符串Hash可以通俗的理解为,把一个字符串转换为一个整数

如果我们通过某种方法,将字符串转换为一个整数,就可以便的确定某个字符串是否重复出现过,这是最简单的字符串Hash应用情景了。

单Hash方法

    unsigned long long Hash[n];
    char s[200005];
    for ( int i=1; i<=n; i++ ) {
        hash[i] = hash[i−1]*p + s[i];
    }

利用unsigned long long的范围自然溢出,相当于自动对pow(2,64)-1 取模,无需再手动取模
 

获取子串的Hash

如果我们求出一个串的Hash,就可以O(1)求解其子串的Hash值。

用 unsigned long long 自动取模的话无需担心。


 

例题1:E、Sort String

题目链接:https://ac.nowcoder.com/acm/problem/17244

题意:给一个长度为len的字符串,头尾相连,定义Si为以 i 为开头的长度为len的字符串。相同的字符串分到一组里,输出len个字符串分成了几组,输出每一组的下标 i ,按照从小到大的顺序。

比如: abca -> abca , bcaa , caab , aabc 

样例:

输入 abab

输出

2
2 0 2
2 1 3

思路:用字符串hash来做,可以O(n)的求出长度为len的len个字符串的hash值,然后用map和vector来处理答案就可以了。

注意:数据比较大,用快输出,用printf 超时了。用unordered_map , 否则超时

代码:

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;
ull base = 233317;
ull pwd[2000006]; // 存hash值
char s[2000006];
vector<int> ans[2000006];
unordered_map<ull,int> mp;
int cnt;

inline void write( int x )
{
    if ( x<0 ) putchar('-'),x=-x;
    if ( x>9 ) write(x/10);
    putchar(x%10+'0');
}

int main()
{
    scanf("%s",s+1); s[0] = '#';
    int len = strlen(s)-1;
    pwd[0] = 0;
    for ( int i=1; i<=len; i++ ) s[i+len] = s[i];
    for ( int i=1; i<=len; i++ ) pwd[i] = pwd[i-1]*base+s[i]; // 先处理出前len项的
    ull base_n = 1;
    for ( int i=1; i<len; i++ ) base_n*=base; // 两个字符距离差了len所差的倍数
    for ( int i=len+1; i<=len+len; i++ ) {
        pwd[i] = (pwd[i-1] - s[i-len]*base_n)*base+s[i]; // pwd存的不是前len项了, 而是从当前i往前数len个的hash值。
    }
    int cnt=1;
    for ( int i=len; i<len*2; i++ ) {  // 将长度为len的字符串都化成了一个数pwd[i], 比较数是否相同就行了
        if ( mp[pwd[i]]==0 ) {
            mp[ pwd[i] ] = cnt++;
            ans[ cnt-1 ].push_back(i-len);
        }
        else ans[ mp[pwd[i]] ].push_back(i-len);
    }
    printf("%d\n",cnt-1);
    for ( int i=1; i<cnt; i++ ) {
        int len = ans[i].size();
        write(len);printf(" ");
        for ( int j=0; j<len-1; j++ ) {
            write(ans[i][j]);  // 快输,用printf超时
            printf(" ");
        }
        write(ans[i][len-1]);printf("\n");
    }

    return 0;
}

 

例题2:Gym-100783J

题目链接:https://vjudge.net/problem/Gym-100783J

题意:在大图里找有几个小图。

思路:二维hash, 具体思路看代码。

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;

ull base=233317; //or 827
ull base2=200379; //or 1127
ull pwd[2005];
char s[2005][2005], t[2005][2005];
int n,m,N,M;

int main()
{
    cin >> n >> m >> N >> M;
    for ( int i=1; i<=n; i++ ) scanf("%s",t[i]+1);
    for ( int i=1; i<=N; i++ ) scanf("%s",s[i]+1);
    ull want = 0;
    for ( int i=1; i<=n; i++ ) {
        ull tmp = 0;
        for ( int j=1; j<=m; j++ ) {
            tmp = tmp*base+t[i][j];
        }
        want = want*base2+tmp;
    }
    for ( int i=1; i<=N; i++ ) {
        pwd[i] = 0;
        for ( int j=1; j<=m; j++ ) {
            pwd[i]=pwd[i]*base+s[i][j];
        }
    }
    ull base_n=1, base_m=1;
    for ( int i=1; i<n; i++ ) base_n=base_n*base2;
    for ( int i=1; i<m; i++ ) base_m=base_m*base;
    int ans=0;
    for ( int j=1; j<=M-m+1; j++ ) {
        ull cur=0;
        for ( int i=1; i<n; i++ ) cur=cur*base2+pwd[i];
        for ( int i=n; i<=N; i++ ) {
            cur = (cur-pwd[i-n]*base_n)*base2+pwd[i];
            if ( cur==want ) ans++;
        }
        for ( int i=1; i<=N; i++ ) {
            pwd[i] = (pwd[i]-s[i][j]*base_m)*base+s[i][j+m];
        }
    }
    cout << ans << endl;

    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值