题目链接:见这里
题意:n个长度为k的字符串,首尾相接,按顺时针顺序写在一张CD上,每个字符串只写一次总字符串长度小于1e6。保证每个字符串不同。这样CD上就有了一个环形字符串,长度是n*k。又给出g个长度为k的字符串列表(编号从1到g),按照顺时针打印CD上字符串的编号。保证每个字符串不同。保证这g个字符串长度的和小于2*1e6。
下面代码和思路参考了Wannafly每日一题,顺便学会了Hash。记录了模板。
解题思路:可以发现,如果枚举第一个串的开始位置,由于输入的g个串,长度都为k,那么每个串的位置就固定了。那么我只要知道,在主串上那一段位置的字符串,是否存在在g个串中,然后如果每个都存在,那么就是符合的。这一部分可以用字符串hash做到O(1)判断。复杂度的话,枚举第一个串的复杂度是k,一共需要匹配n个字符串,所以总复杂度是O(nk)。要注意不能用自然溢出写,因为可以造出数据卡掉自然溢出。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int, int> pii;
const int N = 2e6 + 5;
const int seed = 131;
int mod[2] = {1000000007, 1000000009};
char S[N]; //输入字符串
int F[2][N]; //seed ^ a
int pre[2][N];
int n, g, w;
int tim, vis[N], ans[N];
map <pii, int> cnt;
void pre_init(){ //deal with seed ^ n
for(int t = 0; t <= 1; t++){
F[t][0] = 1;
for(int i = 1; i < N; i++){
F[t][i] = (LL)F[t][i-1] * seed % mod[t];
}
}
}
pii Hash(string s){ //Hash a string
int ret[2] = {0};
int len = s.size();
for(int t = 0; t <= 1; t++){
for(int i = 0; i < len; i++){
ret[t] = ((LL) ret[t] * seed + s[i]) % mod[t];
}
}
return pii(ret[0], ret[1]);
}
pii Hash(int l, int r)
{
int ret[2];
for(int t = 0; t <= 1; t++){
ret[t] = (pre[t][r] - (LL)pre[t][l-1] * F[t][r-l+1] % mod[t] + mod[t]) % mod[t];
}
return pii(ret[0], ret[1]);
}
void pre_solve(){
int len = strlen(S + 1);
for(int t = 0; t <= 1; t++){
pre[t][0] = 0;
for(int i = 1; i <= len; i++){
pre[t][i] = ((LL)pre[t][i-1] * seed + S[i]) % mod[t];
}
for(int j = len + 1, i = 1; i <= w - 1; i++, j++){
pre[t][j] = ((LL)pre[t][j-1] * seed + S[i]) % mod[t];
}
}
}
bool check(int id){
tim++;
int l = id, r = id + w - 1;
for(int i = 1; i <= n; i++, l += w, r += w){
pii t = Hash(l, r);
if(!cnt.count(t)) return 0; //目标串中没有这个子串
int p = cnt[t];
if(vis[p] == tim) return 0; //目标串出现了大于1次
vis[p] = tim; ans[i] = p;
}
printf("YES\n");
for(int i = 1; i <= n; i++) cout << ans[i] <<" "; cout << endl;
return 1;
}
int main()
{
pre_init();
scanf("%d%d%s", &n, &w, S+1);
pre_solve();
scanf("%d", &g);
for(int i = 1; i <= g; i++){
scanf("%s", S);
pii t = Hash(S);
cnt[t] = i;
}
bool mark = 0;
for(int i = 1; i <= w; i++){
if(check(i)){
mark = 1;
break;
}
}
if(mark == 0){
printf("NO\n");
}
return 0;
}