hdu-4821(BKDRHash字符串哈希)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4821

                                                                

String

 

Given a string S and two integers L and M, we consider a substring of S as “recoverable” if and only if 
  (i) It is of length M*L; 
  (ii) It can be constructed by concatenating M “diversified” substrings of S, where each of these substrings has length L; two strings are considered as “diversified” if they don’t have the same character for every position. 

Two substrings of S are considered as “different” if they are cut from different part of S. For example, string "aa" has 3 different substrings "aa", "a" and "a". 

Your task is to calculate the number of different “recoverable” substrings of S.
Input
The input contains multiple test cases, proceeding to the End of File. 

The first line of each test case has two space-separated integers M and L. 

The second ine of each test case has a string S, which consists of only lowercase letters. 

The length of S is not larger than 10^5, and 1 ≤ M * L ≤ the length of S.
Output
For each test case, output the answer in a single line.
Sample Input
3 3
abcabcbcaabc
Sample Output
2

题目大意:有一个字符串s,问有几个满足如下条件的子串:

1、子串的长度为m*L

2、这m个长为L的子子串各个不相等

     基本思路:

用BKDRHash的哈希方法求出字符串的哈希值,然后剩下的就查找有几个满足条件的子串就行了,在记录其中一个m*L的子串时,用map记录有多少不同的长为L的子子串。


#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstdio>
#include <map>
using namespace std;

typedef unsigned long long LL;///无符号形长整数溢出时自动取模
const int MAX=1e5+7,seed=31;
char s[MAX];
LL base[MAX],h[MAX];
map<LL,int>mp;
LL BKDRHash(int l,int r)///这是求每个子串的哈希值的方法
{
    return h[r]-h[l-1]*base[r-l+1];
}
int main()
{
    int m,l;
    base[0]=1;
    for(int i=1;i<=MAX;i++)base[i]=base[i-1]*seed;///基数
    while(~scanf("%d%d",&m,&l))
    {
        int ans=0;
        scanf("%s",s+1);
        int len=strlen(s+1);
        h[0]=0;
        for(int i=1;i<=len;i++)
            h[i]=h[i-1]*seed+s[i]-'a';///对整个字符串求哈希值
        ///下面通过map来记录充计有多少对符合条件的m*l长度的字符串,个人感觉这里类似于尺取法
        for(int i=1;i<=l&&i+m*l-1<=len;i++)///从i开始连续的m*l长度的子串,注意i<=l跳出循环(因为当i==l时,之后的情况都已经查询了)
        {
            mp.clear();
            for(int j=i;j<=i+m*l-1;j+=l)
            {
                LL x=BKDRHash(j,j+l-1);
                mp[x]++;
            }
            if(mp.size()==m)ans++;
            for(int j=i+m*l;j+l-1<=len;j+=l)
            {
                LL x=BKDRHash(j,j+l-1);
                mp[x]++;
                LL y=BKDRHash(j-m*l,j-m*l+l-1);
                mp[y]--;
                if(mp[y]==0)mp.erase(y);
                if(mp.size()==m)ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值