Codeforces Round #291 (Div. 2)C. Watto and Mechanism(hash or 字典树)

题目链接:https://codeforces.com/problemset/problem/514/C

 

题目大意:给n个串,再有m次查询,每次查一个串,问n个串中是否存在串与该串长度相同且恰有一个字母不同

 

题目思路:

1.hash

将n个串通过3的种子转换成hash,以1e8+3为模,a b c分别为0 1 2,然后每个串都会被转换成一个特定的数字。然后对于查询,每次查询都先将查询的串转换成对应的hash值,然后每一位都有对应对hash的影响,减掉后加上假设这一位是另外两个字符时的值,看map中是否有这个值,如果全都没有就是no,有一个有就是yes。由于指数爆炸,所以这种算法在一定概率下还是会导致两个不同的字符串拥有相同的值,所以这种做法还是不推荐。

 

2.字典树

直接将n个字符串插入字典树,然后对查询的串进行dfs,三种情况,要是跟当前字符一样,就不用花改变次数,如果不一样就需要花,如果不一样但是没次数了那就不能继续。如果有串能够遍历到最后,且该串在n个字符串中有,也就是num[root]!=0,那么就是可以。

 

以下是代码:

1.hash

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
using namespace std;
const int MAXN =6e5+5;
const ll MOD = 1e18+3;
int n,m;
char s[MAXN];
map<ll,int>mp;
ll p[MAXN];
ll Hash(char s[]){
    int len=strlen(s);
    ll ans=0;
    rep(i,0,len-1){
        ans=(ans*3ll%MOD+s[i]-'a')%MOD;
    }
    return ans;
}
int main()
{
    p[0]=1;
    rep(i,1,6e5+1){
        p[i]=((p[i-1])*3ll)%MOD;
    }
    while(~scanf("%d%d",&n,&m)){
        mp.clear();
        ll temp;
        rep(i,1,n){
            scanf("%s",s);
            temp=Hash(s);
            mp[temp]=1;
        }
        int flag;
        rep(i,1,m){
            scanf("%s",s);
            flag=0;
            int len=strlen(s);
            ll h=Hash(s);
            rep(j,0,len-1){
                ll temp=(h-(s[len-j-1]-'a')*p[j]+MOD)%MOD;
                rep(k,'a','c'){
                    if(k==s[len-j-1])continue;
                    ll pp=(temp+(k-'a')*p[j]+MOD)%MOD;
                    if(mp[pp]){
                        flag=1;
                        break;
                    }
                }
            }
            if(flag)printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}

 

2.字典树

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN =1e6+5;
int n,m,tot,flag;
int trie[MAXN][3],num[MAXN];
char s[MAXN];
void Insert(char s[]){
    int len=strlen(s);
    int root=0;
    rep(i,0,len-1){
        int t=s[i]-'a';
        if(!trie[root][t])trie[root][t]=tot++;
        root=trie[root][t];
    }
    num[root]++;
}
void dfs(char s[],int root,int l,int r,int p){
    if(l==r&&num[root]){
        if(!p)flag=1;
        return;
    }
    int t=s[l]-'a';
    rep(i,0,2){
        if(t==i){
            if(trie[root][t])dfs(s,trie[root][t],l+1,r,p);
        }
        else{
            if(trie[root][i]&&p)dfs(s,trie[root][i],l+1,r,p-1);
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        memset(num,0,sizeof(num));
        tot=1;
        rep(i,1,n){
            scanf("%s",s);
            Insert(s);
        }
        rep(i,1,m){
            scanf("%s",s);
            flag=0;
            int len=strlen(s);
            dfs(s,0,0,len,1);
            if(flag)printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值