部分问题的题解是自己在比赛时实现的,有些题解的效率并不高,有些题解可能只是因为测试用例太弱而勉强AC,如果有人看到希望能够赐教。 所有题目都使用C++作答
Q1 941. Valid Mountain Array 题意 给出一个数组,判断其是否满足“山脉数组”的条件。(先单调增,到达最大元素,再单调减)
解法 暴力。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution {public : bool validMountainArray (vector<int >& A) { int n = A.size (); if (n < 3 ) { return false ; } bool a = true ; int b = 0 ; bool c = true ; int e = 0 ; for (int i = 0 ; i < n; i++) { if (A[i] > b) { b = A[i]; e = i; } } if (e == 0 || e == (n - 1 )) { c = false ; } for (int i = 0 ; i < e; i++) { if (A[i] >= A[i + 1 ]) { c = false ; } } cout << c << '\n' ; for (int i = e + 1 ; i < n; i++) { if (A[i - 1 ] <= A[i]) { c = false ; } } cout << c << '\n' ; return (a && c); } };
Q2 944. Delete Columns to Make Sorted 题意 给定字符串矩阵,删去不是正序排列的列,留下正序的列,求删去的列数。
解法 暴力。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int minDeletionSize (vector<string>& A) { int m = A.size (); int n = A[0 ].size (); int num = 0 ; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m - 1 ; j++) { if (A[j][i] > A[j + 1 ][i]) { num++; break ; } } } return num; } };
Q3 942. DI String Match 题意 给定只包含”I”(增加)和”D”(减少)的长度为N的字符串,求符合该字符串增减规则的、由整数0至N构成的序列。
解法 个人解法 非常暴力(很丑陋的暴力),非常弱智,非常慢
思路
开一个长度为N+1的数组,令数组最左端的元素为0,其后元素按照DI字符串给出的规则直接模拟产生
找到产生的序列中的最小元素,如果这个元素是负的,则数组中所有元素加上这个元素的绝对值进行修正
循环寻找数组中剩余的最小元素,将整数0 ~ N中未被使用的数放置在对应位置。该过程中开了另一数组记录数字的使用情况(反思:即使要使用这种方法,利用bit mask的方式也更快,更省空间)
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class Solution {public : vector<int > diStringMatch (string S) { int a[10002 ]; int f[10001 ]; int n = S.length (); int b = 10000 ; int c = 0 ; vector<int > d; memset (a, 0 , sizeof a); memset (f, 0 , sizeof f); for (int i = 0 ; i < n; i++) { if (S[i] == 'I' ) { a[i + 1 ] = a[i] + 1 ; } else { a[i + 1 ] = a[i] - 1 ; } } for (int i = 0 ; i <= n; i++) { if (a[i] < b) { b = a[i]; c = i; } } if (b < 0 ) { for (int i = 0 ; i <= n; i++) { a[i] -= b; } } f[c] = 1 ; for (int i = 1 ; i <= n; i++) { int e = 10000 ; c = 0 ; for (int j = 0 ; j <= n; j++) { if ((a[j] <= e) && (f[j] == 0 )) { e = a[j]; c = j; } } f[c] = 1 ; a[c] = i; } for (int i = 0 ; i <= n; i++) { d.push_back (a[i]); cout << d[i] << " " ; } return d; } };
解法 from 酒井算协 解1 利用减治法,按照规则排列好前N-1个之后,如果第N个字符是增加,则直接把N放在末尾。 如果第N个字符是减少,则前面所有数字+1,再把0放在末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public : vector<int > diStringMatch (string s) { int n = s.size (); vector<int > ans; ans.push_back (0 ); for (int i = 1 ; i <= n; i++) { if (s[i - 1 ] == 'I' ) { ans.push_back (i); } else { for (int j = 0 ; j < i; j++) { ans[j]++; } ans.push_back (0 ); } } return ans; } };
解2 直接模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { public : vector<int > diStringMatch (string s) { int n = s.size (), min_val = 1500 , max_val = 1500 ; vector<int > ans; ans.resize (n + 1 ); ans[0 ] = 1500 ; for (int i = 1 ; i <= n; i++) { int tval = ans[i - 1 ]; if (s[i - 1 ] == 'I' ) { tval = ++max_val; } else { tval = --min_val; } ans[i] = tval; } for (int i = 0 ; i <= n; i++) { ans[i] -= min_val; } return ans; } };
Q4 943. Find the Shortest Superstring 题意 给定一个字符串数组,求一个最短的字符串使得字符串数组中的每个字符串都是其子串。字符串数组的各个字符串数组之间不互为子串。
解法 动态规划。
大体思路 将所有字符串放在一行,使得这些字符串重叠的部分(overlap)最大。
状态定义 dp[mask][i]。 mask是一个bit mask,表示了当前的串由哪些字符串组成(mask的第i位表示A[i]是否包含在当前字符串中,0为不包含,1为包含)。而i是指上一次被加入到当前串的字符串为A[i]。dp[mask][i]则表示了包含了由mask标识的字符串组成,且上一个被加入的字符串为A[i]的当前串的最大overlap。
状态转移 dp[mask ^ (1 << j)][j] = max(overlap[A[i]][A[j]] + dp[mask][i]。其中mask的第j位(bit)应该为0。(计算当前状态下加入新字符串A[j]所能得到的最大overlap,从状态dp[mask][i]转移到dp[mask ^ (1 << j)][j])。
初始状态 dp所有元素都为0
算法步骤:
对于所有可能的i,j排列,计算overlap[i][j]。
对于所有可能出现的mask,计算dp[mask][i],用parent二维数组对上述状态转移过程中j的上一个字符串下标i进行追踪
利用parent中包含的信息重新构建出答案
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 class Solution { public : int dp[1 << 12 ][21 ], parent[1 << 12 ][21 ], overlap[21 ][21 ]; string shortestSuperstring (vector<string>& A) { memset (dp, 0 , sizeof (dp)); int n = A.size (); for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { if (i == j) { continue ; } string s1 = A[i], s2 = A[j]; for (int k1 = 0 ; k1 < s1.size (); k1++) { bool ok = true ; for (int k2 = 0 ; k1 + k2 < s1.size () && k2 < s2.size (); k2++) { if (s1[k1 + k2] != s2[k2]) { ok = false ; break ; } } if (ok) { overlap[i][j] = s1.size () - k1; break ; } } } } for (int i = 1 ; i < (1 << n); i++) { for (int j = 0 ; j <= n; j++) { if ((i >> j) & 1 ) { int pmask = i ^ (1 << j); for (int k = 0 ; k <= n; k++) { if ((pmask >> k) & 1 ) { int val = dp[pmask][k] + overlap[k][j]; if (dp[i][j] == 0 || dp[i][j] < val) { dp[i][j] = val; parent[i][j] = k; } } } } } } int mask = (1 << n) - 1 ; int val = dp[mask][0 ], mark = 0 ; for (int i = 0 ; i < n; i++) { if (dp[mask][i] > val) { val = dp[mask][i]; mark = i; } } vector<int > seq; while (mask) { seq.push_back (mark); int nxt = parent[mask][mark]; mask ^= (1 << mark); mark = nxt; } string ans = "" ; for (int i = seq.size () - 1 ; i>= 0 ; i--) { string s = A[seq[i]]; if (i == seq.size () - 1 ) { ans += s; } else { int over = overlap[seq[i + 1 ]][seq[i]]; for (int j = over; j < s.size (); j++) { ans += s[j]; } } } return ans; } };