题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5771
题目大意:题目定义对任意两个串X , Y,如果存在一个字符串Z满足XZ=ZY,那么这两个串就是完美匹配的。
现在给你一个长串A(长度为n),短串B(长度为m),
总共q次查询,每次查询给你一个起点x,和长度y
要你求出有多少个字符串Z可以使 在长串A中以x为起点长度为m的子串a 和 短串B 满足BZ=Za。(要求加上字符串Z之后串的长度不能超过y)。
题目思路:对于任意的两个串X和Y,如果要满足完美匹配,一定会满足如下的条件
在求出断点之后,我们只需要加上X串的最小循环节的长度倍数,得到的仍旧是解。
那么我们就可以先预处理出短串B所有的断点,并将断点前的串补到串的最后的情况,用hash值存储,在每次查询时,只需要判断短串所有断点的情况中是否有与长串A的子串相同的串就行了,如果有,就利用最小循环节找出所有小于长度y的情况就是答案了。
这里有一个处理出短串B所有的断点移动情况的方法:
我们知道一个字符串前 i 位的hash值为 H[i]=H[i-1]*hash+s[i],我们令P[i]=P[i-1]*hash,设字符串的长度为len,那么整个串的hash值为H[len];
如果我们把当前字符串的第一个字符移动到最后一个字符的后面,那么当前串的hash值就会变为
H=H[len]*has+s[1]-s[1]*P[len] (这是根据hash值转移的式子分解出来的,这里就不详解了)
那么我们就可以通过这个式子,将第二个字符,第三个字符...直到最后一个字符移动到串的最后面的所有情况的hash值都求出来了,时间复杂度为O(len)
具体实现可以看代码:
#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define MP make_pair
#define FIN freopen("in.tx","r",stdin)
#define FOUT freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>pii;
typedef pair<LL,LL>pll;
const int inf = 0x3f3f3f3f;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
const int MX = 1e5+7;
const int has = 99959;
int n,m,q;
int nxt[MX];
int a[MX];
char str1[MX],str2[MX];
LL P1[MX],P2[MX];
pll H[MX];//本题的数据比较厉害,用普通的hash会被卡,得用双重hash来存储才能过。
unordered_map<LL,int>mp;
void Hash(){
//预处理出str1的hash值,方便求解子串的hash值
P1[0]=P2[0]=1;
for(int i=1;i<=n;i++){
P1[i]=(P1[i-1]*has)%mod1;
P2[i]=(P2[i-1]*has)%mod2;
LL h1=((H[i-1].fi*has)%mod1+(LL)str1[i])%mod1;
LL h2=((H[i-1].se*has)%mod2+(LL)str1[i])%mod2;
H[i]=MP(h1,h2);
}
}
LL get_hash(int l,int r){
//得到子串str1[l,r]的hash值
LL h1=(H[r].fi-(H[l-1].fi*P1[r-l+1])%mod1+mod1)%mod1;
LL h2=(H[r].se-(H[l-1].se*P2[r-l+1])%mod2+mod2)%mod2;
return h1+h2;
}
int main(){
int T;cin>>T;
while(T--){
scanf("%d%d%d",&n,&m,&q);
scanf("%s%s",str1+1,str2+1);
mp.clear();
Hash();
nxt[0]=-1;
for(int i=1;i<=m;i++){
int p=nxt[i-1];
while(p>=0 && str2[p+1]!=str2[i]) p=nxt[p];
nxt[i]=p+1;
}
int len=m-nxt[m];
if(nxt[m]*2<m) len=m;
//求出短串的最小循环节len
pll h=MP(0,0);
for(int i=1;i<=m;i++){
LL h1=((h.fi*has)%mod1+(LL)str2[i])%mod1;
LL h2=((h.se*has)%mod2+(LL)str2[i])%mod2;
h=MP(h1,h2);
}
int cnt=0;
for(int i=1;i<=m;i++){
//这里处理出短串str2所有断点移动后的串的hash值
LL h1=((h.fi*has)%mod1+(LL)str2[i])%mod1;
LL h2=((h.se*has)%mod2+(LL)str2[i])%mod2;
h=MP((h1-(LL)str2[i]*P1[m]%mod1+mod1)%mod1,(h2-(LL)str2[i]*P2[m]%mod2+mod2)%mod2);
if(!mp[h.fi+h.se]){
mp[h.fi+h.se]=++cnt;
a[cnt]=i;
//a记录每个断点的长度;
//注意:可能不同的断点会形成相同的串,相同的串肯定是取越短的长度解才更优
}
}
while(q--){
int j;LL k;
scanf("%d%lld",&j,&k);
LL hh=get_hash(j,j+m-1);//得到子串的hash值
int p=mp[hh];
if(!p)
printf("0\n");
else
printf("%lld\n",(LL)(k-a[p]+len)/len);
//所有可行的串都满足a[p]+q*len<=k,这时找到最大的q就是答案了
}
}
return 0;
}