牛客 白兔的字符串 哈希预处理匹配字符串

题目链接:牛客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 S1Sn(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;
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值