题目链接:牛客15253 白兔的字符串
抓住2020年8月的小尾巴发一篇水题解,又是新学期开始了^ _ ^
题目描述
白兔有一个字符串T。白云有若干个字符串S1,S2…Sn。
白兔想知道,对于白云的每一个字符串,它有多少个子串是和T循环同构的。
提示:对于一个字符串a,每次把a的第一个字符移动到最后一个,如果操作若干次后能够得到字符串b,则a和b循环同构。
所有字符都是小写英文字母
输入描述:
第一行一个字符串T ( ∣ T ∣ < = 1 0 6 ) (|T|<=10^6) (∣T∣<=106) 第二行一个正整数n ( n < = 1000 ) (n<=1000) (n<=1000) 接下来n行为 S 1 − S n ( ∣ S 1 ∣ + ∣ S 2 ∣ + … + ∣ S n ∣ < = 1 0 7 ) , m a x ( ∣ S 1 ∣ , ∣ S 2 ∣ , ∣ S 3 ∣ , ∣ S 4 ∣ , . . ∣ S n ∣ ) < = 1 0 6 S1-Sn (|S1|+|S2|+…+|Sn|<=10^7),max(|S1|,|S2|,|S3|,|S4|,..|Sn|)<=10^6 S1−Sn(∣S1∣+∣S2∣+…+∣Sn∣<=107),max(∣S1∣,∣S2∣,∣S3∣,∣S4∣,..∣Sn∣)<=106
输出描述:
输出n行表示每个串的答案
示例1
输入
abab 2
abababab
ababcbaba
输出
5
2
题解
求子串中含有多少个循环同构。
很明显,需要预处理出各种循环同构的情况,所以可以对需要匹配的字符串进行*2操作,也就是自己加一次自己,这样就可以从左到右每次选出子串长度进行hash化。
我们可以把每次hash值存在一个数组v中,对数组v进行排序,可以进行二分查询,这样就可以把查询复杂度降到
l
o
g
n
log_n
logn。
每次输入一个字符串进行匹配的时候,需要重新求hash值,故可以直接暴力从左到右,每一次选出len长的子串在数组v进二分查询,若存在即有一个循环同构。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
const int MAX=1e6+7;
unsigned ll base=1,hs[MAX<<1];
char s[MAX<<1],str[MAX<<1];
vector<unsigned ll>v;
int main(){
ios_base::sync_with_stdio(0);cin.tie(0);
cin>>(s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++)base=base*131;
for(int i=1;i<=len;i++)s[i+len]=s[i];
for(int i=1;i<=2*len;i++)hs[i]=hs[i-1]*131+s[i]-'a'+1;
for(int i=1;i<=len;i++)v.push_back(hs[i+len-1]-((unsigned ll)(i>0?hs[i-1]:0))*base);
sort(v.begin(),v.end());
int n;cin>>n;
while(n--){
cin>>(s+1);
int l2=strlen(s+1),ans=0;
for(int i=1;i<=l2;i++) hs[i]=hs[i-1]*131+s[i]-'a'+1;
for(int i=1;i+len-1<=l2;i++){
unsigned ll cnt=hs[i+len-1]-((unsigned ll)(i>0?hs[i-1]:0))*base;
int id=lower_bound(v.begin(),v.end(),cnt)-v.begin();
if(id!=v.size()&&v[id]==cnt)ans++;
}
cout<<ans<<endl;
}
return 0;
}