HDU 3336 Count the string 所有前缀在串中的出现总次数

题目链接

题意

给定一个串 S ,求其所有前缀在其中的出现次数的总和。

思路

考虑 fail 数组, fail[i]=j 的含义是 S[0..j1]==S[ij..i1] .

dp[i] 为以 i 结尾的串中与前缀相同的串的个数。

fail[i]=j S[0..j1]==S[ij..i1]
而在 S[0..j1] 中又有 S[0..km]==S[j1km,j1],0mdp[j]
故有 S[0..ki]==S[i1ki,i1],0mdp[j]

S[0..j1] 中出现的所有与前缀相同的串,其在 S[ij,i1] 中也出现,即 dp[i] 至少等于 dp[j] .

另外,其本身 S[0..i1] 也是一个前缀,所以再 +1 .

最后,是否有遗漏呢?
假设漏算了一些串,它们对应前缀的结束位置在 j1 之后,那么, fail[i]>j ,矛盾。

dp[i] 即为上面所提的两部分之和, dp[i]=dp[j]+1 ,表示以 i 结尾的串中与前缀相同的串的个数。

最后求个 ni=1dp[i] ,即为答案。

Code

#include 
#define maxn 200010
using namespace std;
const int mod = 10007;
typedef long long LL;
char s[maxn];
int f[maxn], n, dp[maxn];
void getfail() {
    f[0] = f[1] = 0;
    for (int i = 1; i < n; ++i) {
        int j = f[i];
        while (j && s[i] != s[j]) j = f[j];
        f[i+1] = s[i] == s[j] ? j+1 : 0;
    }
}
void work() {
    scanf("%d%s", &n, s);
    getfail();
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        (dp[i] = dp[f[i]] + 1) %= mod;
        (ans += dp[i]) %= mod;
    }
    printf("%d\n", ans);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

你可能感兴趣的:(字符串,kmp,hdu)