题目:传送门
题目:给你三个字符串a, b, c,他们都是字符串 s 的连续子串,字符 '?' 可以是任何字符,问字符串 s 的长度最小是多少。
1 <= len(a),len(b),len(c) <= 2000
思路:
很容易的可以想到一个贪心,就是,按照 a -> b -> c 这样的顺序去构造 s(共有六种方式,这里只列举一种),每次都尽可能的将 b 左移,将 c 左移, 这里有一个反例。
例如: a = ??c?, b = ac?a,c = ?b?a
按照上面的贪心策略,就是:
1、??c?(字符串a)
2、acca(字符串a和b合并,得到最优的ab)
3、accab?a(最优的ab和字符串c合并)
但是,最优的构造方式是:
1、??c?(字符串 a )
2、?ac?a(字符串 a 和 b 合并,得到 ab,b[0] 和 a[1] 对应)
3、?acbaa(字符串 ab 和字符串 c 合并,c[0] 和 ab[2] 对应)
所以这种贪心策略是错误的。
我们可以预处理字符串 a 和字符串 b 的匹配情况
ab[i]:表示,b 相对于 a 移动了 i 之后,是不是能匹配,例如我们假设 a = 12345, b = 2345,
那么 ab[1] 就是 a[1] 和 b[0] 对应,然后后面的也一 一对应是不是能匹配
12345
2345
b[0] 和 a[1] 对应,b[1] 和 a[2] 对应,b[2] 和 a[3] 对应,b[3] 和 a[4] 对应,这样的对应,i - j 都是等于 1 的( i 是字符串 a 对应的下标,j 是字符串 b 对应的下标),所以只要有一个不等,就将 ab[1] 置为 1,表示不匹配。
按照同样的思路,预处理出 ac 和 bc
然后,就枚举 ab 的偏移量和 ac 的偏移量,判断是否能更新答案就行了。
复杂度 o(n^2)
#include <bits/stdc++.h> #define LL long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) using namespace std; const int N = 2e4 + 5, up = 4000; char a[N], b[N], c[N]; bool ab[N], ac[N], bc[N]; int main() { scanf("%s", a); scanf("%s", b); scanf("%s", c); int lena = strlen(a); int lenb = strlen(b); int lenc = strlen(c); rep(i, 0, lena - 1) rep(j, 0, lenb - 1) /// 预处理 ab if(!(a[i] == b[j] || a[i] == '?' || b[j] == '?')) ab[i - j + up] = 1; rep(i, 0, lena - 1) rep(j, 0, lenc - 1) /// 预处理 ac if(!(a[i] == c[j] || a[i] == '?' || c[j] == '?')) ac[i - j + up] = 1; rep(i, 0, lenb - 1) rep(j, 0, lenc - 1) /// 预处理 bc if(!(b[i] == c[j] || b[i] == '?' || c[j] == '?')) bc[i - j + up] = 1; int ans = INF; rep(i, -4000, 4000) rep(j, -4000, 4000) if(!ab[i + up] && !ac[j + up] && !bc[j - i + up]) { ///b相对于a偏移了i,c相对于i偏移了j,c相对于b偏移了j - i int L = min({0, i, j}); int R = max({lena, lenb + i, lenc + j}); ans = min(ans, R - L); } printf("%d ", ans); return 0; }