51Nod 1753 相似子串

本文介绍了一种利用哈希和二分法判断字符串相似性的算法。通过预处理字符串的哈希值前缀和,并使用并查集维护等价关系,可以高效地判断两个子串是否在给定规则下相似。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:
两个字符串相似定义为:
1.两个字符串长度相等
2.两个字符串对应位置上有且仅有至多一个位置所对应的字符不相同
给定一个字符串,每次询问两个子串在给定的规则下是否相似。给定的规则指每次给出一些等价关系,如‘a'=’b',‘b'=’c'等,注意这里的等价关系具有传递性,即若‘a'=’b',‘b'=’c',则‘a'=’c'。

解题报告:
正解:哈希+二分
我们先处理出每一个字母哈希值的前缀和,然后并查集维护等价关系,对于相似的判断,如果两串的哈希值完全一样,就为YES,如果存在一个字符不一样,那么我们就二分那个位置,判断左右哈希值是否相等,如果两端哈希值都不相等,说明存在两个以上的位置不相同,判为NO

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=300005;
ll p=20021021,mod=1e9+7,ha[N][26],mul[N];
char S[N],sc[5];int n,a[N],fa[27];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
ll query(int l,int r){
    ll ret=0,x;
    for(int i=0;i<26;i++){
        x=((ha[r][i]-(ha[l-1][i]*mul[r-l+1])%mod)+mod)%mod;
        ret=(ret+x*(find(i)+1))%mod;
    }
    return ret;
}
void work()
{
    scanf("%s",S+1);
    n=strlen(S+1);
    for(int i=1;i<=n;i++)a[i]=S[i]-'a';
    mul[0]=1;for(int i=1;i<=n;i++)mul[i]=mul[i-1]*p%mod;
    for(int i=1;i<=n;i++){
        for(int j=0;j<26;j++)
            ha[i][j]=ha[i-1][j]*p%mod;
        (ha[i][a[i]]+=1)%=mod;
    }
    int Q,Ls,Rs,Lt,Rt,m,l,r,mid,Hl,Hr,Hll,Hrr,flag;
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d%d%d%d",&m,&Ls,&Rs,&Lt,&Rt);
        for(int i=0;i<26;i++)fa[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%s",sc);
            if(find(sc[0]-'a')!=find(sc[1]-'a'))
                fa[find(sc[1]-'a')]=find(sc[0]-'a');
        }
        if(Rt-Lt!=Rs-Ls){puts("NO");continue;}
        if(query(Lt,Rt)==query(Ls,Rs)){puts("YES");continue;}
        l=1;r=Rt-Lt+1;flag=0;
        while(l<=r){
            mid=(l+r)>>1;
            Hl=query(Ls,Ls+mid-1);Hr=query(Lt,Lt+mid-1);
            Hll=query(Ls+mid,Rs);Hrr=query(Lt+mid,Rt);
            if(Hl!=Hr && Hll!=Hrr){flag=1;break;}
            if(Hl!=Hr)r=mid-1;
            else l=mid+1;
        }
        if(flag)puts("NO");
        else puts("YES");
    }
}

int main()
{
    work();
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/7565452.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值