D. Tavas and Malekas DFS模拟 + kmp + hash || kmp + hash

http://codeforces.com/contest/535/problem/D

 

如果真的要把m个串覆盖上一个串上面,是可以得,不会超时。

要注意到一点,全部覆盖后再判断时候合法,和边放边判断,结果是一样的,后者还更难做到。

 

那么就是先按顺序把串覆盖上去,已经存在的就不去覆盖了,然后kmp一次记录匹配位置,判断即可。

用DFS覆盖,DFS回溯的时候记录一个数组tonext[i]表示第i个点的第一个空位是tonext[i],这样覆盖上去就是O(n)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e6 + 20;
const int MOD = 1e9 + 7;
int tonext[maxn];
int ret;
char str[maxn];
char sub[maxn];
void dfs(int cur, int k, int from) {
    if (k < 0) return;
    if (k == 0) {
        ret = cur;
        return;
    }
    if (tonext[cur] != cur) {
        ret = tonext[cur];
        dfs(tonext[cur], k - (tonext[cur] - cur), from + tonext[cur] - cur);
        tonext[cur] = ret;
    } else {
        ret = cur + 1;
        str[cur] = sub[from];
        dfs(cur + 1, k - 1, from + 1);
        tonext[cur] = ret;
    }
}
int a[maxn];
bool HASH[maxn];
int kmpnext[maxn];
void get_next(char sub[], int lensub) {
    int i = 1, j = 0;
    kmpnext[1] = 0;
    while (i <= lensub) {
        if (j == 0 || sub[i] == sub[j]) {
            kmpnext[++i] = ++j;
        } else j = kmpnext[j];
    }
    return;
}
void kmp(int lenstr, int lensub) {
    int i = 1, j = 1;
    while (i <= lenstr) {
        if (j == 0 || str[i] == sub[j]) {
            ++i;
            ++j;
        } else j = kmpnext[j];
        if (j == lensub + 1) {
            HASH[i - lensub] = true;
            j = kmpnext[j];
        }
    }
    return;
}
void work() {
    int lenstr, m;
    scanf("%d%d", &lenstr, &m);
    for (int i = 1; i <= lenstr; ++i) {
        tonext[i] = i;
        str[i] = 'A';
//        printf("f");
    }
//    printf("%c\n", str[1]);
    scanf("%s", sub + 1);
    int lensub = strlen(sub + 1);
    for (int i = 1; i <= m; ++i) {
        scanf("%d", &a[i]);
        dfs(a[i], lensub, 1);
    }
    str[lenstr + 1] = '\0';
//    printf("%s\n", str + 1);
    get_next(sub, lensub);
    kmp(lenstr, lensub);
    for (int i = 1; i <= m; ++i) {
        if (!HASH[a[i]]) {
            cout << 0 << endl;
            return;
        }
    }
    LL ans = 1;
    for (int i = 1; i <= lenstr; ++i) {
        if (str[i] == 'A') {
            ans *= 26;
            if (ans >= MOD) ans %= MOD;
        }
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

 

开始的时候想不到直接kmp

用的其实就是kmp的next数组,不断next[next[]]

就是记录所有的前缀和后缀相等。

对于abc****abc,也就是前后缀相等的话,长度是3

记上一个覆盖到的位置是pos[i - 1] + lensub - 1

然后如果这个和他没交集,就算,如果有,要判断。怎么判断呢?

算出交集大小,如果交集刚好是3,那么是可以得。否则,是NO

交集是3说明后缀的那3个和前缀匹配了。

注意m可能是0

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e6 + 20;
const int MOD = 1e9 + 7;
char sub[maxn];
int tonext[maxn];
int pos[maxn];
bool book[maxn];
void get_next(char sub[], int lensub) {
    int i = 1, j = 0;
    tonext[1] = 0;
    while (i <= lensub) {
        if (j == 0 || sub[i] == sub[j]) {
            tonext[++i] = ++j;
        } else j = tonext[j];
    }
    return;
}
void work() {
    int lenstr, m;
//    cin >> lenstr >> m;
//    cin >> sub + 1;
    scanf("%d%d", &lenstr, &m);
    scanf("%s", sub + 1);
    int lensub = strlen(sub + 1);
    get_next(sub, lensub);
    for (int i = 1; i <= m; ++i) {
//        cin >> pos[i];
        scanf("%d", &pos[i]);
    }
    int t = tonext[lensub + 1];
    while (t != 1) {
        book[t - 1] = true;
        t = tonext[t];
    }
    int total = lenstr - lensub;
    if (m == 0) {
        total += lensub;
    }
    for (int i = 2; i <= m; ++i) {
        int to = pos[i - 1] + lensub - 1;
        int haha = to - pos[i] + 1;
        if (haha <= 0) {
            total -= lensub;
            continue;
        }
        if (!book[haha]) {
            printf("0\n");
            return;
        }
        total -= lensub - haha;
    }
    LL ans = 1;
    for (int i = 1; i <= total; ++i) {
        ans *= 26;
        if (ans >= MOD) ans %= MOD;
    }
    printf("%I64d\n", ans);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

posted on 2016-12-08 15:30  stupid_one  阅读(157)  评论(0编辑  收藏  举报

导航