题目链接: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|<=107),max(|S1|,|S2|,|S3|,|S4|,..|Sn|)<=106
输出描述:
输出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
",ans);
}
return 0;
}