P7469 [NOI Online 2021 提高组] 积木小赛 题解
简要题意:
给定两个字符串 \(A,B\)
求出 \(B\) 的本质不同子串中是 \(A\) 的子序列的个数
首先考虑将 子串 转化为后缀的前缀,我们可以枚举整个后缀来获取整个 \(B\) 的 子串。
枚举后缀是 \(O(n)\) 的,再用整个后缀去与 \(A\) 匹配。
不妨设这个后缀为 \(S_{1-i}\)
因为 \(S_{1-i}\) 是 \(A\) 的子序列,那么 \(S_{1-(i-1)}\) 也一定是 \(A\) 的子序列。
于是可以 \(O(n)\) 模拟得到这个后缀在 \(A\) 中的最长匹配长度。
而整个后缀的前缀中,只要 \(\leq\) 这个长度,一定都是 \(A\) 的子序列。
总体复杂度 \(O(n^2)\),但是答案要求的是本质不同的子串。
所以用一个双哈希判重即可。(考场上写了3个哈希。。)
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD=19260919,base=173,MOD2=23532139,base2=239,MOD3=33532139,base3=337;
const int N=3005;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)) f|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;
}
template <typename T>
inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10^48);
}
int n,pi[N],ans;
string a,b,tep,ptr,res;
pair<ll,ll> str[N*N];
int head[MOD+2],Next[N*N],tot;
inline void Add(int u,pair<ll,ll> ins){Next[++tot]=head[u],str[tot]=ins,head[u]=tot;}
inline bool Find(int u,pair<ll,ll> goal){
for(register int i=head[u];i;i=Next[i]){
if(str[i].first==goal.first&&str[i].second==goal.second) return true;
}
return false;
}
ll has1,has2,has3;
int main(){
// freopen("block.in","r",stdin);
// freopen("block.out","w",stdout);
read(n);
cin>>a>>b;
for(register int st=0;st<n;++st){
ptr=b.substr(st,n-st);
int loc=0;
for(register int i=0;i<n;++i){if(a[i]==ptr[loc]) loc++;}
res="";has1=0,has2=0,has3=0;
for(register int i=0;i<loc;++i){
res+=ptr[i];
has1=(has1*base+ptr[i]-'0')%MOD;
has2=(has2*base2+ptr[i]-'0')%MOD2;
has3=(has3*base3+ptr[i]-'0')%MOD3;
if(!Find(has1,make_pair(has2,has3))){
Add(has1,make_pair(has2,has3));
ans++;
}
// cout<<res<<endl;
}
}
print(ans);
// puts("");
// printf("%.6lf\n",(clock()-st)/CLOCKS_PER_SEC);
return 0;
}
/*
5
bcabc
bbcca
*/