链接:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

白兔有一个字符串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

题解:

hash题
好久没打都忘得差不多了
讲哈希的博客
根据题意,我们可以得知,要寻找与T是循环同构的子串
那T也就相当于是环,我们拆环为链,长度加倍,
然后将二倍的T中每一部分取哈希,每一部分的长度就是原T的长度
然后根据大小进行排序
输入子串a后,因为是在中取连续子串,所以本身就是一条链,我们按照T的长度对a的每一部分取hash然后比较是否相等
比较大小的方法可以用二分查找,因为我们之前已经对大小进行排序了;也可以模拟map,直接用map会超时

代码:

因为我的代码写太乱了,找了大佬条理清晰的代码。
并加上详细注释(应该很详细了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e6 + 5;
ull hs[maxn];
ull base[maxn]; 
char T[maxn<<1],s[maxn];
int n;
vector<ull> v;
ull gethash(int i,int j){
   //获得子串的hash 
    return hs[j]-hs[i-1]*base[j-i+1];
}
//查找
bool judge(int i,int j){
   
    ull x=gethash(i,j);//获得子串的hash 
    int id=lower_bound(v.begin(),v.end(),x)-v.begin();//二分查找 
    if(id==v.size())return false;//如果没找到 
    else return v[id]==x;//判断是否找到 
}
int main(){
   
    base[0]=1;
    for(int i=1;i<maxn;i++)
        base[i]=base[i-1]*131;
    cin>>(T+1);
    ll len=strlen(T+1);

    for(int i=1;i<=len;i++)
        T[i+len]=T[i];//拆环为链,长度加倍 
        
    for(int i=1;i<=2*len;i++)
        hs[i]=hs[i-1]*131+(T[i] - 'a' + 1);//对链进行hash 
        
    for(int i=1;i<=len;i++)
        v.push_back(gethash(i,i+len-1));//获得子串的hash 
    
	sort(v.begin(),v.end());//对哈希结果进行排序 ,方便后面的二分查找 
    
	cin>>n;
    while(n--){
   
        cin>>(s+1);
        ll lens=strlen(s+1);
        int cnt=0;
        
        for(int i=1;i<=lens;i++)
            hs[i]=hs[i-1]*131+(s[i] - 'a' + 1);//进行hash 
            
        for(int i=1;i+len-1<=lens;i++)
            if(judge(i,i+len-1))cnt++;//判断子串是否与T循环同构 
        cout<<cnt<<endl;
    }
    return 0;
}