HDU 6153 A Secret 套路,求解前缀在本串中出现的次数

http://acm.hdu.edu.cn/showproblem.php?pid=6153

首先相当于翻转两个串,然后求s2前缀在s1中出现的次数。

这是一个套路啦

首先把两个串结合起来,中间加一个'%'之类的分割

设dp[i]表示前缀1---i在本串中的出现次数和

那么从后开始dp,所有dp值一开始都是1,表示前缀出现了一次,就是自己本身。

转移,设当前去到第i位,则dp[next[i + 1] - 1] += dp[i]

就是ABACABA这样,已经知道了ABACABA出现了一次,然后前后缀ABA和ABA重复出现,那么dp[3]肯定能够加上dp[7]的,dp[7]包含了dp[7]个"ABA",就这个意思。

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = 2e6 + 20;
int nextliu[maxn], dp[maxn];
char str[maxn], sub[maxn];
void get_next (char str[], int nextliu[], int lenstr) {
    int i = 1, j = 0;
    nextliu[1] = 0;
    while (i <= lenstr) {
        if (j == 0 || str[i] == str[j]) {
            nextliu[++i] = ++j;
        } else j = nextliu[j];
    }
    return ;

}
const int MOD = 1e9 + 7;
int dp2[maxn];
void work() {
    scanf("%s%s", str + 1, sub + 1);
    int lenstr = strlen(str + 1), lensub = strlen(sub + 1);
    reverse(str + 1, str + 1 + lenstr);
    reverse(sub + 1, sub + 1 + lensub);
    int to = lensub + 1;
    sub[to++] = '$';
    for (int i = 1; i <= lenstr; ++i) sub[to++] = str[i];
    sub[to] = '';
    to--;
    for (int i = 1; i <= lensub; ++i) dp[i] = 0;
    for (int i = lensub + 2; i <= to; ++i) dp[i] = 1;
    get_next(sub, nextliu, to);
    for (int i = to; i >= 1; --i) {
        int t = nextliu[i + 1] - 1;
        dp[t] += dp[i];
        dp[t] %= MOD;
    }
//    printf("%d
", dp[2] - dp2[2]);
    LL ans = 0;
    for (int i = 1; i <= lensub; ++i) {
        ans += 1LL * i * dp[i] % MOD;
        ans %= MOD;
    }
    printf("%I64d
", ans);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}
View Code

其实也可以用后缀数组 + dp搞,不过TLE了,应该要用DC3,不去搞了

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

http://acm.gdufe.edu.cn/Problem/read/id/1338

原文地址:https://www.cnblogs.com/liuweimingcprogram/p/7398502.html