字符串哈希(双模数哈希)-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,若能,输出依次选择的顺序。 给定一个由n个长度为k的子串组成的n×k的字符串s,以及g个长度为k的字符串。 判断:是否能够在这g个串中,选择n个串拼接成s,若能,输出依次选择的顺序。
数据范围:
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<=1000000;g∈[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先复制,拓展成“环”。 接着对输入的g个字符串分别哈希,将哈希值存入到map当中,再分枚举s的起点,求s的n个长度为k的子串的哈希值。 若这n个哈希值均在map中,说明可以构成,按顺序输出即可。
需 要 说 明 的 是 : ① 、 由 于 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会返回一个不确定的值。 需要说明的是: ①、由于k的长度可能会比较小,冲突的概率会比较大,所以取双模数,用pair存储哈希值。 ②、枚举s的起点时,仅需枚举k次,这是因为每个子串的长度均为k,从k+1处枚举与从1位置枚举等价。 ③、两种原因会导致失败,一是s中某个长度为k的子串的哈希值不在这g个字符串中;二是g个字符串中的每个串仅能使用一次,若同一起点开始,枚举到的子串的哈希值在map中且已经被使用过,也是不满足条件的。这个问题可以用数组st来记录同一起点枚举的过程中,各个被使用的子串对应的这个起点。 ④、bool函数忘记return会返回一个不确定的值。
代码:
#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;
}