思路还算明白,不过要落实到代码上还真敲不出来。
题意:
有两个由大写字母组成的颜色序列,将它们合并成一个序列:每次可以把其中一个序列开头的颜色放到新序列的尾部。
对于每种颜色,其跨度定义为合并后的序列中最后一次和第一次出现的位置之差,求所有合并方案中所有颜色跨度之和的最小值。
分析:
d(i, j)表示两个串分别已经移走了i个和j个字符。那么无论新串的顺序是什么,有多少种颜色已经开始但尚未结束是确定的,记为c(i, j)。再继续移走一个字符,所有颜色跨度之和就增加c(i, j)。c的计算是通过记录每种颜色在每个串的始末位置来确定的。
由于数据量较大,还用滚动数组来优化空间复杂度。
1 //#define LOCAL 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 8 const int maxn = 5000 + 10; 9 const int INF = 1000000000; 10 char p[maxn], q[maxn]; 11 int sp[26], ep[26], sq[26], eq[26]; //记录两个串每种颜色的始末 12 int d[2][maxn], c[2][maxn]; //c[i][j]表示d[i][j]时还有多少种颜色已经开始但尚未结束 13 14 int main(void) 15 { 16 #ifdef LOCAL 17 freopen("1625in.txt", "r", stdin); 18 #endif 19 20 int n, m, T; 21 scanf("%d", &T); 22 while(T--) 23 { 24 scanf("%s", p+1); 25 scanf("%s", q+1); 26 n = strlen(p+1); 27 m = strlen(q+1); 28 for(int i = 1; i <= n; ++i) p[i] -= 'A'; 29 for(int i = 1; i <= m; ++i) q[i] -= 'A'; 30 31 for(int i = 0; i < 26; ++i) { sp[i] = sq[i] = INF; ep[i] = eq[i] = 0; } 32 for(int i = 1; i <= n; ++i) 33 { 34 sp[p[i]] = min(sp[p[i]], i); 35 ep[p[i]] = i; 36 } 37 for(int i = 1; i <= m; ++i) 38 { 39 sq[q[i]] = min(sq[q[i]], i); 40 eq[q[i]] = i; 41 } 42 43 int t = 0; 44 memset(d, 0, sizeof(d)); 45 memset(c, 0, sizeof(c)); 46 for(int i = 0; i <= n; ++i) 47 { 48 for(int j = 0; j <= m; ++j) 49 { 50 if(!i && !j) continue; 51 52 int v1, v2; 53 v1 = v2 = INF; 54 if(i) v1 = d[t^1][j] + c[t^1][j]; 55 if(j) v2 = d[t][j-1] + c[t][j-1]; 56 d[t][j] = min(v1, v2); 57 //计算c 58 if(i) 59 { 60 c[t][j] = c[t^1][j]; 61 if(sp[p[i]] == i && sq[p[i]] > j) c[t][j]++; 62 if(ep[p[i]] == i && eq[p[i]] <= j) c[t][j]--; 63 } 64 else if(j) 65 { 66 c[t][j] = c[t][j-1]; 67 if(sq[q[j]] == j && sp[q[j]] > i) c[t][j]++; 68 if(eq[q[j]] == j && ep[q[j]] <= i) c[t][j]--; 69 } 70 } 71 t ^= 1; 72 } 73 printf("%d ", d[t^1][m]); 74 } 75 76 return 0; 77 }