CF - 494B - 字符串预处理 + DP

本文详细解析 CodeForces 494B 的字符串匹配问题,通过预处理和动态规划的方法高效求解,提供完整的 C++ 实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

problem:

http://codeforces.com/problemset/problem/494/B

给你两个字符串。s 和 t , 长度不超过1e5

问有多少1, a2, ..., ak 和 b1, b2, ..., bk 序列对满足以下:

  • k ≥ 1
  •   t is a substring of string saisai + 1... sbi (string s is considered as 1-indexed).
input
ababa
aba
output
5
input
welcometoroundtwohundredandeightytwo
d
output
274201
input
ddd
d
output
12

think:

1. 首先要解读题意。有点抽象。这个看一下样例就好了。

第一个样例:

1,3

1,4

1,5

2,5

3,5
第三个样例:
1,1
1,2
1,3
2,2
2,3
3,3
12,12
12,13
13,13
13,23
23,23
123,123
比如13,23表示 a = 1,3  b = 2,3 

2. 预处理pre数组,表示第i个字符,前面,最靠后的可以和他sub的位置。

3. 前 j 位是一起的。。。然后从 j+1 到 i 组成一个新的(因为他要求 ai > b(i-1))

前j位是dp[j]
从j+1到i是 pri[i] - j 。 因为新加进来的左区间是是 j+1 ~ pri[i]  右区间是 i
然后把这两块乘起来,dp[j] * (pre[i]-j)
然后枚举所有可能的 j  然后展开一下,就O(n)了,
然后加上pri[i], 表示数组只有 1 个,没有 j 
   综上
dp[i] = sum(dp[j] * (pre[i] - j), j < pre[i]) + pre[i] = sum(dp[j]) * pre[i] - sum(dp[j] * j) + pre[i] = sum1[pre[i]-1] * pre[i] - sum2[pre[i] - 1] + pre[i]
sum1[i] = sum[i-1] + dp[i]
sum2[j] = sum[i-1] + dp[i] * i

code:

const int mod = 1000000007;
const int N = 100100;

char a[N];
char b[N];
int pre[N];
int dp[N];
int sum1[N];
int sum2[N];
int fail[N];
int match[N];
int nb, na;

void get_fail(int *fail, const char *p){
    fail[0] = -1;
    for(int i = 1; p[i]; ++i){
        int j = fail[i-1];
        while(j != -1 && p[j+1] != p[i]) j = fail[j];
        fail[i] = (p[i] == p[j+1] ? j + 1 : -1);
    }
}

void kmp(const char *a, const char *b, const int na, const int nb){
    match[0] = (a[0] == b[0] ? 0 : -1);
    if(match[0] == nb - 1) pre[1] = 1;
    else pre[1] = 0;
    for(int i = 1; a[i]; ++i){
        int j = match[i-1];
        if(j == nb - 1) j = fail[j];
        while(j != -1 && a[i] != b[j+1]) j = fail[j];
        match[i] = (a[i] == b[j+1] ? j + 1 : -1);
        if(match[i] == nb - 1) pre[i + 1] = i + 2 - nb;
        else pre[i + 1] = pre[i];
    }
}

int main(){
    scanf("%s%s", a, b);
    int na = strlen(a);
    int nb = strlen(b);
    get_fail(fail, b);
    kmp(a, b, na, nb);
    int ans = 0;
    for(int i = 1; i <= na; ++i) if(pre[i] > 0){
        dp[i] = (LL)sum1[pre[i]-1] * pre[i] % mod;
        dp[i] = (dp[i] - sum2[pre[i]-1] + mod) % mod;
        dp[i] = (dp[i] + pre[i]) % mod;
        ans = (ans + dp[i]) % mod;
        sum1[i] = (sum1[i-1] + dp[i]) % mod;
        sum2[i] = (sum2[i-1] + (LL)dp[i] * i % mod) % mod;
    }
    printf("%d\n", ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值