Codeforeces 727 E 字符串双Hash 2017/1/12

来源:互联网 发布:改良圈算法 matlab 编辑:程序博客网 时间:2024/05/04 01:18

题目链接:见这里
题意: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 ^ aint 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;}
0 0
原创粉丝点击