1、题面:
2、题解:
现将字符串转化为一个径B函数处理过的字符串,bs,bt,
(B函数:如果当前字符i前面有和它相同的字符j,B(i) = i-j,否组B(i) = len,
这样处理通过记录字符串的前驱,使字符串所有相同字符和不同字符的相对位置都确定下来,相当于一个链表,
所以如果字符串Q,P是相似子串,则B(Q) = B(P)。)
然后遍历求出bs串的长度为m的子串,判断是否有字符串bs’与bt串的哈希值相同。
注意,这里可以寻找S[i,j]与S[i+1,j+1]之间的关系,如果前进了一位,质询要考虑被删除的字符S[i]与
它后面第一个与S【i】相同的字符s[j](就是S[i]的后继),的关系,只有j的相对位置发生变化,所以修改B(j)的值即可。
(感谢阜神学长的代码)
官方题解:
3、代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const ull base = 133;
const int maxn = 1e6+10;
int s[maxn],bs[maxn],t[maxn],bt[maxn],pre[maxn],nxt[maxn],n,m;
struct Hash{
int hs[maxn],p[maxn];
void Insert(int a[],int len){
p[0] = 1;hs[0] = 0;
for(int i=1;i<=len;i++){
p[i] = p[i-1]*base;
hs[i] = hs[i-1]*base+a[i];
}
}
ull Get_Hash(int l,int r){
return hs[r]-hs[l-1]*p[r-l+1];
}
}S,T;
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&s[i]);
if(pre[s[i]]==0){
bs[i] = n;//转化为B数组
}
else bs[i] = i-pre[s[i]];
pre[s[i]] = i;
}
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++){
scanf("%d",&t[i]);
if(pre[t[i]]==0){
bt[i] = n;//转化为B数组
}
else bt[i] = i-pre[t[i]];
pre[t[i]] = i;
}
for(int i=1;i<maxn;i++) pre[i] = n+1;//记录前驱
for(int i=n;i>=1;i--){
nxt[i] = pre[s[i]]; //记录后缀
pre[s[i]] = i;
}
S.Insert(bs,n);
T.Insert(bt,m);
int ans = 0;
ull ans1 = T.Get_Hash(1,m),ans2 = S.Get_Hash(1,m);
if(ans1==ans2) ans++;//比较哈希值
for(int l=2,r=m+1;r<=n;l++,r++){
ans2 = S.Get_Hash(l,r);
int bor = nxt[l-1];
if(bor<=r){
ans2 = ans2+(n-bs[bor])*S.p[(r-l+1)-(bor-l+1)];//修改后面的哈希值
}
if(ans2==ans1) ans++;
}
printf("%d\n",ans);
return 0;
}