zoukankan      html  css  js  c++  java
  • HDU4821 字符串哈希+尺取

    题目链接:String

    题目大意

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

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

    数据范围

    S长度不超过100000, 1ML 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从 1lenML+1, 然后内层枚举从i开始,M个长度为L的串是否有重复值,如果没有答案就++,之后就TLE,其实复杂度是 (lenML+1)M * map(log), TLE也确实正常。之后才知道i只需要枚举从1L就行,然后内层枚举所有的长度为L的子串,并将它们的Hash顺序存入数组,之后采用尺取法,每M个看是否有M个不同的元素,如果是则答案++,此复杂度就到了O(LLen/L)log(ULL)即为O(lenlog)

    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
    ", res);
        }
        return 0;
    }

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

  • 相关阅读:
    URAL 2067 Friends and Berries (推理,数学)
    URAL 2070 Interesting Numbers (找规律)
    URAL 2073 Log Files (模拟)
    URAL 2069 Hard Rock (最短路)
    URAL 2068 Game of Nuts (博弈)
    URAL 2066 Simple Expression (水题,暴力)
    URAL 2065 Different Sums (找规律)
    UVa 1640 The Counting Problem (数学,区间计数)
    UVa 1630 Folding (区间DP)
    UVa 1629 Cake slicing (记忆化搜索)
  • 原文地址:https://www.cnblogs.com/TRDD/p/9813514.html
Copyright © 2011-2022 走看看