1. 程式人生 > 實用技巧 >牛客-白兔的字串(Hash+二分)

牛客-白兔的字串(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;
}