题目大意
给出两个字符串 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+m−1],它左侧和右侧可能存在多个和它相交的子串,为了尽可能的减少替换次数,但是只能覆盖其中一侧而无法兼顾左右,这一点容易证明。考虑去覆盖它以及它右侧和它相交的所有子串,那么需要在区间 j ≤ k ≤ j + m − 1 j \leq k \leq j + m - 1 j≤k≤j+m−1找到所有的 k k k 使得 [ k , k + m − 1 ] [k, k + m - 1] [k,k+m−1]是一个子串。当覆盖 j ≤ k ≤ j + m − 1 j \leq k \leq j + m - 1 j≤k≤j+m−1中的任何一个子串时,这段连续多个交错的子串可以全部被覆盖,下文将简称下图所示的这种交错子串为交错段( m = 6 m=6 m=6)。
但是对于整个字符串,存在多个这样的交错段,考虑DP状态转移,首先预处理阶段将所有满足 [ i , i + m − 1 ] [i,i + m- 1] [i,i+m−1] 为子串的 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+m−1]), 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]+m−1]且[a[k],a[k]+m−1]是一个子串 },他们可以从前面小于 a [ i ] + m − 1 < a [ j ] a[i] + m - 1 < a[j] a[i]+m−1<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;
}