Codeforces1729 G. Cut Substrings(线性DP)

传送门


题目大意

给出两个字符串 s , t s, t s,t,在 s s s 中找到若干个不相交的子串 t t t,将这些子串全部替换为字符串"…"( ∣ t ∣ |t| t个),使得替换后的 s s s 不含有子串 t t t。例如 s s s = “abababacababa”, t t t = “aba”,一种可行的替换方式为:“ab…bac…ba”。需要求出最少需要替换多少个子串满足替换后的 s s s 不含有子串 t t t,以及有多少种替换方案。两种方案子串的集合完全相同视为同一种方案。

解题思路

本题首先确定一个贪心的思想:对于 s s s 中的一个子串 [ s j , s j + m − 1 ] [s_j, s_{j + m- 1}] [sj,sj+m1],它左侧和右侧可能存在多个和它相交的子串,为了尽可能的减少替换次数,但是只能覆盖其中一侧而无法兼顾左右,这一点容易证明。考虑去覆盖它以及它右侧和它相交的所有子串,那么需要在区间 j ≤ k ≤ j + m − 1 j \leq k \leq j + m - 1 jkj+m1找到所有的 k k k 使得 [ k , k + m − 1 ] [k, k + m - 1] [k,k+m1]是一个子串。当覆盖 j ≤ k ≤ j + m − 1 j \leq k \leq j + m - 1 jkj+m1中的任何一个子串时,这段连续多个交错的子串可以全部被覆盖,下文将简称下图所示的这种交错子串为交错段( m = 6 m=6 m=6)。

在这里插入图片描述

但是对于整个字符串,存在多个这样的交错段,考虑DP状态转移,首先预处理阶段将所有满足 [ i , i + m − 1 ] [i,i + m- 1] [i,i+m1] 为子串的 i i i 另存在一个数组 a a a 中, a [ j ] a[j] a[j] 指代的即为字符串 s s s 的下标 i i i

f [ i ] f[i] f[i] 表示对于字符串所有前缀子串 [ 1 , i ] [1, i] [1,i] 最少覆盖的次数(这里对应整个字符串的前缀 [ 1 , i + m − 1 ] [1, i + m - 1] [1,i+m1]), g [ i ] g[i] g[i] 表示方案数。对于上述交错段的 { k k k a [ k ] ∈ [ a [ j ] , a [ j ] + m − 1 ] 且 [ a [ k ] , a [ k ] + m − 1 ] a[k] \in [a[j], a[j] + m - 1] 且[a[k], a[k] + m - 1] a[k][a[j],a[j]+m1][a[k],a[k]+m1]是一个子串 },他们可以从前面小于 a [ i ] + m − 1 < a [ j ] a[i] + m - 1 < a[j] a[i]+m1<a[j]的最大的 i i i 的状态进行转移,转移的方式为:

  • f [ k ] > f [ i ] + 1 f[k] > f[i] + 1 f[k]>f[i]+1 ,则 f [ k ] = f [ i ] + 1 f[k] = f[i] + 1 f[k]=f[i]+1 g [ k ] = g [ i ] g[k] = g[i] g[k]=g[i]
  • f [ k ] = = f [ i ] + 1 f[k] == f[i] + 1 f[k]==f[i]+1,则 g [ k ] = g [ k ] + g [ i ] g[k] = g[k] + g[i] g[k]=g[k]+g[i]

状态数组初始化应该为 f [ i ] = i n f f[i] = inf f[i]=inf,但比较麻烦的是转移初始状态如何设置。因为状态转移是按交错段进行的,正常来说应该对整个字符串的第一个交错段全部初始化为 f [ i ] = 0 , g [ i ] = 1 f[i] = 0, g[i] = 1 f[i]=0,g[i]=1,方便起见可以对添加一个起点并初始化为 f [ i ] = 0 , g [ i ] = 1 f[i] = 0, g[i] = 1 f[i]=0,g[i]=1;答案应该从整个字符串的最后一个交错段中找,为了方便可以添加一个终点,终点从最后一个交错段转移而来,因此最终需要将这种方式得到的答案减一。

参考链接

https://codeforces.com/blog/entry/106916
https://blog.csdn.net/CM_20030210/article/details/126854204

//
//  main.cpp
//  codeforce
//  Created by Happig on 2022/9/15.
//

#include <iostream>
#include <string>
#include <stack>
#include <algorithm>
#include <string.h>
#include <vector>

using namespace std;
#define ENDL "\n"
typedef long long ll;
const int Mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 505;

int n, m, num;
char s[maxn], t[maxn];
int a[maxn];
ll f[maxn], g[maxn];

int check(int l, int r) {
    for(int i = l; i <= r; i++) {
        if(s[i] != t[i - l + 1]) return 0;
    }
    return 1;
}

void pre() {
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    n = strlen(s + 1);
    m = strlen(t + 1);
    num = 0;
    for(int i = 1; i <= n - m + 1; i++) {
        if(check(i, i + m - 1)) {
            a[++num] = i;
        }
    }
    a[0] = -inf;
    a[++num] = n + m;
}

void work() {
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    g[0] = 1;
    for(int i = 0, j; i < num; i++) {
        j = i + 1;
        while(j <= num  && a[j] <= a[i] + m - 1) j++;
        for(int k = j; k <= num && a[k] <= a[j] + m - 1; k++) {
            if(f[k] > f[i] + 1) {
                f[k] = f[i] + 1;
                g[k] = g[i];
            } else if(f[k] == f[i] + 1) {
                g[k] = (g[k] + g[i]) % Mod;
            }
        }
    }
    cout << f[num] - 1 << " " << g[num] << ENDL;
}


int main() {
    int T;
    cin >> T;
    while(T--) {
        pre();
        work();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值