zoukankan      html  css  js  c++  java
  • Wannafly挑战赛11 D 白兔的字符串 Hash

    Wannafly挑战赛11

    D   白兔的字符串

    白兔有一个字符串T。白云有若干个字符串S1,S2..Sn

    白兔想知道,对于白云的每一个字符串,它有多少个子串是和T循环同构的。

    提示:对于一个字符串a,每次把a的第一个字符移动到最后一个,如果操作若干次后能够得到字符串b,则a和b循环同构。

    所有字符都是小写英文字母

    输入描述:

    第一行一个字符串T(|T|<=10^6)
    第二行一个正整数n (n<=1000)
    接下来n行为S1~Sn (|S1|+|S2|+…+|Sn|<=10^7),max(|S1|,|S2|,|S3|,|S4|,..|Sn|)<=10^6

    输出描述:

    输出n行表示每个串的答案
    示例1

    输入

    abab
    2
    abababab
    ababcbaba

    输出

    5
    2

    tags:字符串 Hash

    把 T 在 Hash 后存在 set 里,再对每个 S 找子串。但要用 unordered_set ,否则会超时。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    typedef  unsigned long long  ull;
    const int N = 1000005, Base=1e9+7;
    
    unordered_set< ull > Set;
    ull  p[N];
    void Init(char* T)
    {
        int len = strlen(T);
        ull  Hash = 0;
        p[0] = 1;
        rep(i,0,len-1) {
            Hash = Hash*Base+T[i];
            p[i+1] = p[i]*Base;
        }
        rep(i,0,len-1) {
            Hash = (Hash-T[i]*p[len-1])*Base + T[i];
            Set.insert(Hash);
        }
    }
    int solve(char* S, int lenT)
    {
        int lenS = strlen(S);
        if(lenS < lenT) return 0;
        ull  Hash = 0;
        int ret = 0;
        rep(i,0,lenT-1) Hash = Hash*Base+S[i];
        if(Set.count(Hash)) ++ret;
        rep(i,lenT,lenS-1) {
            Hash = (Hash-S[i-lenT]*p[lenT-1])*Base + S[i];
            if(Set.count(Hash)) ++ret;
        }
        return ret;
    }
    char T[N], S[N];
    int main()
    {
        scanf("%s", T);
        Init(T);
        int lenT = strlen(T);
        int n;  scanf("%d", &n);
        while(n--)
        {
            scanf("%s", S);
            printf("%d
    ", solve(S, lenT));
        }
    
        return 0;
    }
    View Code

    正解是:

    标算是ex_kmp。用R[i]表示S[i..len]和T的最长公共前缀,L[i]表示S[1..i]和T的最长公共后缀,当L[i]+R[i+1]>=|T|时,可以给一段区间打上标记。如果T的一个表示法在某个位置出现则至少会被一个标记覆盖。然后就变成了区间加法,求有多少个正数。用差分+前缀和即可。

  • 相关阅读:
    2020.05.02【NOIP普及组】模拟赛C组31总结
    【提高组NOIP2008】传纸条 题解
    【NOIP2006PJ】Jam的计数法(count)题解
    话说placeholder
    css垂直居中
    fixed和absolute的区别
    链接的属性href=“?” ?该些什么及优缺点
    论ul、ol和dl的区别
    笔记本插拔电源黑屏一下
    CSS 样式书写规范
  • 原文地址:https://www.cnblogs.com/sbfhy/p/8552524.html
Copyright © 2011-2022 走看看