HDU4821 字符串哈希+尺取

HDU4821 字符串哈希+尺取

题目链接:String

题目大意

给你一个字符串S,问你满足下面两个条件的子串有多少个?

  • 子串的长度必须是M×L;
  • M个长度为L的串必须互不相同,两个字符串不同是指任意一个位置不相同就算作不同。

数据范围

S长度不超过100000, S的长度。

解题思路

首先可以采用字符串Hash将字符串处理便于储存,字符串Hash我是在网上找的一个方法,实际上是找一个基数Base,然后采用Base进制,可以采用模一个大质数,也可以是直接采用unsigned long long 爆了后自动模。这里是采用 unsigned long long。此题可以记下前缀Hash值,之后如果要找区间[l,r]这个串的Hash值,只需要Hash[r] - Hash[l - 1] * Pow[r - l + 1]。这是因为对于一个几进制数,比如10进制的12345,现在我已经求得Hash[1] = 1, Hash[2] = 12, Hash[3] = 123, Hash[4] = 1234, Hash[5] = 12345,, 然后我想求[3,5]的Hash值,那么因为是10进制一眼可以知道是345,那么计算机只能通过Hash[5] - Hash[2] * Pow[3];而Pow[3]即10的3次方,即为12345 - 12 × 1000 = 345,因此,可以知道其他进制也是满足此公式。

处理完字符串的Hash之后,我首先想的是外层枚举i从 , 然后内层枚举从i开始,M个长度为L的串是否有重复值,如果没有答案就++,之后就TLE,其实复杂度是 * map(log), TLE也确实正常。之后才知道i只需要枚举从就行,然后内层枚举所有的长度为L的子串,并将它们的Hash顺序存入数组,之后采用尺取法,每M个看是否有M个不同的元素,如果是则答案++,此复杂度就到了即为

AC代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;
const int maxn = 100000;
const ULL base = 233ULL;
ULL Hash[maxn + 5], Pow[maxn + 5];
ULL num[maxn + 5];
map<ULL, int>vis;
char s[maxn + 5];
int M, L, tot, res;
ULL Get_Hash(int l, int r) {
    return Hash[r] - Pow[r - l + 1] * Hash[l - 1];
}
void solve() {
    int cnt = 0;
    vis.clear();
    for(int i = 1; i <= M; i++) {
        if(!vis[num[i]])cnt++;
        vis[num[i]]++;
    }
    if(cnt == M)res++;
    int r = M + 1, l = 1;
    while(r <= tot) {
        vis[num[l]]--; if(vis[num[l]] == 0)cnt--; l++;
        if(vis[num[r]] == 0)cnt++; vis[num[r]]++; r++;
        if(cnt == M)res++;
    }
}
int main() {    
    Pow[0] = 1;
    for(int i = 1; i <= maxn; i++)Pow[i] = Pow[i - 1] * base;
    while(~scanf("%d%d", &M, &L)) {
        res = 0; scanf("%s", s + 1);
        int len = strlen(s + 1);
        Hash[0] = 1;
        for(int i = 1; i <= len; i++)Hash[i] = Hash[i - 1] * base + (ULL)(s[i] - 'a' + 1);
        for(int i = 1; i <= L; i++) {
            tot = 0;
            for(int j = i; j <= (len - L + 1); j += L)num[++tot] = Get_Hash(j, j + L - 1);
            solve();
        }
        printf("%d\n", res);
    }
    return 0;
}

另外基数Base要大于s[i],即满足进制规则。

posted @ 2018-07-12 10:36 呵呵!!! 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
字符串哈希滑动窗口是一种用于处理字符串的算法。它主要用于在给定的字符串中找到满足特定条件的子串。 在字符串哈希滑动窗口算法中,我们首先计算原始字符串哈希值。然后,我们使用一个滑动窗口来遍历字符串,每次滑动一个固定长度的窗口。我们可以通过比较每个窗口内的子串的哈希值来判断是否满足条件。 具体而言,我们可以使用BKDRHash哈希函数来计算字符串哈希值。然后,我们枚举每个可能的起点,并使用滑动窗口来计算窗口内的子串的哈希值。通过比较窗口内的子串的哈希值,我们可以判断是否满足条件。 对于滑动窗口的移动,如果窗口内的子串满足条件,我们可以继续将窗口往右移动一个固定的长度。如果窗口内的子串不满足条件,我们将窗口的右边界移到最右端,并依次比较新窗口内的子串的哈希值。 综上所述,字符串哈希滑动窗口算法是通过计算字符串哈希值,并使用滑动窗口来遍历字符串,以找到满足特定条件的子串。这个算法可以高效地处理字符串,并且能够应用于各种字符串相关的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [String字符串哈希+滑动窗口)](https://blog.csdn.net/weixin_43872264/article/details/107571742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【字符串hash+滑动窗口】String HDU - 4821](https://blog.csdn.net/qq_45599865/article/details/111143633)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值