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
*/

上一篇:询系--家庭作业


下一篇:支配树口胡