牛客-白兔的字符串(Hash+二分)

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

题目描述

白兔有一个字符串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行表示每个串的答案

输入
abab
2
abababab
ababcbaba

输出
5
2

既然是循环同构,那么我们可以将串T展开成2|T|的长度,然后用Hash保存每种T长度下的值,接下来我们在遍历以下n个字符串的时候可以直接查找上面是否出现了字符串,如果有的话那么ans++,至于查找的方式,用map的话比较慢,会T掉,用二分就可以了(我们先排好序),实际上极限的理论复杂度是\(O(log|T|\sum S)\),也会T掉的。。。只不过很少有代码会跑到这种极限复杂度的。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int mac=2e6+10;

char ss[mac],s[mac];
ull hashs[mac],wei[mac];
int base=131;

int main()
{
    scanf ("%s",ss);
    int len=strlen(ss);
    for (int i=0; i<len; i++)
        ss[len+i]=ss[i];
    ull hash=0,cnt=0;
    wei[0]=1;
    for (int i=1; i<mac; i++)
        wei[i]=wei[i-1]*base;
    int num=0;
    for (int i=0; i<len*2-1; i++){
        hash=hash*base+ss[i];
        cnt++;
        if (cnt==len){
            cnt--;
            hashs[++num]=hash;
            hash=hash-wei[len-1]*ss[i-len+1];
        }
    }
    sort(hashs+1,hashs+num+1);
    int n;
    scanf ("%d",&n);
    for (int i=1; i<=n; i++){
        scanf ("%s",s);
        int lens=strlen(s);
        int ans=0;
        hash=0,cnt=0;
        for (int i=0; i<lens; i++){
            hash=hash*base+s[i];
            cnt++;
            if (cnt==len){
                cnt--;
                int pos=lower_bound(hashs+1,hashs+num+1,hash)-hashs-1;
                if (pos<num && hashs[pos+1]==hash) ans++;
                hash=hash-wei[len-1]*s[i-len+1];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lonely-wind-/p/13370585.html