字符串哈希(双模数哈希)-Games on a CD-CodeForces-727E

字符串哈希(双模数哈希)-Games on a CD-CodeForces-727E

题意:

给 定 一 个 由 n 个 长 度 为 k 的 子 串 组 成 的 n × k 的 字 符 串 s , 以 及 g 个 长 度 为 k 的 字 符 串 。   判 断 : 是 否 能 够 在 这 g 个 串 中 , 选 择 n 个 串 拼 接 成 s , 若 能 , 输 出 依 次 选 择 的 顺 序 。 给定一个由n个长度为k的子串组成的n×k的字符串s,以及g个长度为k的字符串。\\ \ \\判断:是否能够在这g个串中,选择n个串拼接成s,若能,输出依次选择的顺序。 nkn×ksgk gns

数据范围:

n , k ∈ [ 1 , 100000 ] , 且 n × k < = 1000000 ; g ∈ [ n , 100000 ] , 且 g × k < = 2000000 。 n,k∈[1,100000],且n×k<=1000000;g∈[n,100000],且g×k<=2000000。 n,k[1,100000]n×k<=1000000g[n,100000]g×k<=2000000

Sample Input:
3 1
abc
4
b
a
c
d

Sample Output:
YES
2 1 3 


Sample Input:
4 2
aabbccdd
4
dd
ab
bc
cd

Sample Output:
NO

题解:

首 先 将 s 先 复 制 , 拓 展 成 “ 环 ” 。   接 着 对 输 入 的 g 个 字 符 串 分 别 哈 希 , 将 哈 希 值 存 入 到 m a p 当 中 , 再 分 枚 举 s 的 起 点 , 求 s 的 n 个 长 度 为 k 的 子 串 的 哈 希 值 。   若 这 n 个 哈 希 值 均 在 m a p 中 , 说 明 可 以 构 成 , 按 顺 序 输 出 即 可 。 首先将s先复制,拓展成“环”。\\ \ \\接着对输入的g个字符串分别哈希,将哈希值存入到map当中,再分枚举s的起点,求s的n个长度为k的子串的哈希值。\\ \ \\若这n个哈希值均在map中,说明可以构成,按顺序输出即可。 s gmapssnk nmap

需 要 说 明 的 是 :   ① 、 由 于 k 的 长 度 可 能 会 比 较 小 , 冲 突 的 概 率 会 比 较 大 , 所 以 取 双 模 数 , 用 p a i r 存 储 哈 希 值 。   ② 、 枚 举 s 的 起 点 时 , 仅 需 枚 举 k 次 , 这 是 因 为 每 个 子 串 的 长 度 均 为 k , 从 k + 1 处 枚 举 与 从 1 位 置 枚 举 等 价 。   ③ 、 两 种 原 因 会 导 致 失 败 , 一 是 s 中 某 个 长 度 为 k 的 子 串 的 哈 希 值 不 在 这 g 个 字 符 串 中 ; 二 是 g 个 字 符 串 中 的 每 个 串 仅 能 使 用 一 次 , 若 同 一 起 点 开 始 , 枚 举 到 的 子 串 的 哈 希 值 在 m a p 中 且 已 经 被 使 用 过 , 也 是 不 满 足 条 件 的 。 这 个 问 题 可 以 用 数 组 s t 来 记 录 同 一 起 点 枚 举 的 过 程 中 , 各 个 被 使 用 的 子 串 对 应 的 这 个 起 点 。   ④ 、 b o o l 函 数 忘 记 r e t u r n 会 返 回 一 个 不 确 定 的 值 。 需要说明的是:\\\ \\①、由于k的长度可能会比较小,冲突的概率会比较大,所以取双模数,用pair存储哈希值。\\\ \\②、枚举s的起点时,仅需枚举k次,这是因为每个子串的长度均为k,从k+1处枚举与从1位置枚举等价。\\\ \\③、两种原因会导致失败,一是s中某个长度为k的子串的哈希值不在这g个字符串中;\\\qquad二是g个字符串中的每个串仅能使用一次,\\\qquad若同一起点开始,枚举到的子串的哈希值在map中且已经被使用过,也是不满足条件的。\\\qquad这个问题可以用数组st来记录同一起点枚举的过程中,各个被使用的子串对应的这个起点。\\\ \\④、bool函数忘记return会返回一个不确定的值。  kpair skkk+11 skgg使map使st使 boolreturn


代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#define ll long long
#define P pair<ll,ll>
#define x first
#define y second
using namespace std;
const int N=2e6+10;
const int base=131;
const int mod1=1e9+7;
const int mod2=1e9+9;
int n,k,g;
ll h[N][3],p[3];
char s[N],t[N];
map<P,int> M;
int ans[N],st[N];

bool check(int pos)
{
    int l=pos,r=pos+k-1;
    for(int i=1;i<=n;i++)   
    {
        ll h1=(h[r][1]-h[l-1][1]*p[1]%mod1+mod1)%mod1;
        ll h2=(h[r][2]-h[l-1][2]*p[2]%mod2+mod2)%mod2;
        P tmp=make_pair(h1,h2);
        if(!M.count(tmp)||st[M[tmp]]==pos) return false;
        ans[i]=M[tmp];
        st[ans[i]]=pos;
        l+=k,r+=k;
    }
    return true;
}

int main()
{
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=len+1;i<=2*len;i++) s[i]=s[i-len];   ///
    len=2*len,s[len+1]='\0';

    p[1]=p[2]=1;
    for(int i=1;i<=k;i++)
        p[1]=p[1]*base%mod1,
        p[2]=p[2]*base%mod2;

    for(int i=1;i<=len;i++)
    {
        h[i][1]=(h[i-1][1]*base%mod1+s[i]-'a')%mod1,
        h[i][2]=(h[i-1][2]*base%mod2+s[i]-'a')%mod2;
    }

    scanf("%d",&g);
    for(int i=1;i<=g;i++)  ///计算g个子串的哈希值
    {
        scanf("%s",t+1);
        ll h1=0,h2=0;
        for(int j=1;j<=k;j++)
            h1=(h1*base%mod1+t[j]-'a')%mod1,
            h2=(h2*base%mod2+t[j]-'a')%mod2;

        M[make_pair(h1,h2)]=i;
    }

    for(int i=1;i<=k;i++)  ///枚举起点位置
        if(check(i))
        {
            puts("YES");
            for(int i=1;i<=n;i++) printf("%d ",ans[i]);
            puts("");
            return 0;
        }

    puts("NO");

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值