周赛链接 :https://leetcode-cn.com/contest/weekly-contest-226/
唉,今天做题的状态非常糟糕,几天不刷题手感都没有了。
5665. 从相邻元素对还原数组【medium】(https://leetcode-cn.com/problems/restore-the-array-from-adjacent-pairs/)
这道题的目的是根据相邻元素对得到符合条件的原始数组,原始元素的数量为n,adjacentPairs的相邻元素对就为 n - 1。
解法1:LinkedList双向队列
1.首先将第一个相邻元素对内的元素加入队列,第一个元素对就已经使用过了;
2.从相邻元素对中没有剩余没有使用过的元素对中找到包含队首或者对位的元素,将当前元素对剩余元素添加至队首或队尾,当前元素就标记为已经使用过;
3.队列元素的数量 < n 时重复步骤2;
// 跑通 39/46 的示例,运行超时
class Solution {
public int[] restoreArray(int[][] adjacentPairs) {
int n = adjacentPairs.length + 1;
LinkedList<Integer> quque = new LinkedList();
quque.offer(adjacentPairs[0][0]);
quque.offer(adjacentPairs[0][1]);
boolean[] visited = new boolean[n - 1];
visited[0] = true;
while (quque.size() < n){
for (int i = 1; i < adjacentPairs.length; i++) {
if (visited[i]){
continue;
}
if (quque.getFirst() == adjacentPairs[i][0] || quque.getFirst() == adjacentPairs[i][1]){
quque.addFirst(quque.getFirst() == adjacentPairs[i][0] ? adjacentPairs[i][1] : adjacentPairs[i][0]);
visited[i] = true;
}else if (quque.getLast() == adjacentPairs[i][0] || quque.getLast() == adjacentPairs[i][1]){
quque.addLast(quque.getLast() == adjacentPairs[i][0] ? adjacentPairs[i][1] : adjacentPairs[i][0]);
visited[i] = true;
}
}
}
int[] res = new int[n];
for (int i = 0; i < n; i++){
res[i] = quque.get(i);
}
return res;
}
}
这个代码的时间复杂度为O(n^2),部分用例运行超时,可以想办法减少一下复杂度。
看一下给的例子,可以发现,原始数组两端的元素在相邻元素对数组当中必然只出现一次,其他的元素则会出现两次。可以根据这个性质,先找到两端的元素,在往中间添加元素。
解法二:
class Solution {
// 必定有两个结果
public int[] restoreArray(int[][] adjacentPairs) {
int n = adjacentPairs.length + 1;
Map<Integer, List<Integer>> map = new HashMap<>(); // 用于记录相邻元素对,key表示前元素,value表示后一元素列表
for (int[] adjacentPair : adjacentPairs){
putAdjacentPair(0, adjacentPair, map);
putAdjacentPair(1, adjacentPair, map);
}
int[] res = new int[n];
int i = 0;
for (Integer key : map.keySet()){
if (map.get(key).size() == 1){ // 只出现一次的key必定为原始数组两头
res[i] = key;
i = n - 1 - i;
}
}
// 此时i=0
int j = n - 1;
while ((++i) <= (--j)){
res[i] = (i == 1 || res[i - 2] != map.get(res[i - 1]).get(0)) ? map.get(res[i - 1]).get(0) : map.get(res[i - 1]).get(1);
res[j] = (j == n - 2 || res[j + 2] != map.get(res[j + 1]).get(0)) ? map.get(res[j + 1]).get(0) : map.get(res[j + 1]).get(1);
}
return res;
}
void putAdjacentPair(int index, int[] adjacentPair, Map<Integer, List<Integer>> map){
if (!map.containsKey(adjacentPair[index])){
map.put(adjacentPair[index], new ArrayList<>());
}
map.get(adjacentPair[index]).add(adjacentPair[1 - index]);
}
}
因为HashMap的时间复杂度为O(1),所以这个解的时间复杂度达到了O(n)级别。
5666. 回文串分割【hard】(https://leetcode-cn.com/problems/palindrome-partitioning-iv/)
感觉这道题还没有达到hard的水平,使用动态规划和双指针的思路这道题就很好解。
1.首先使用一个boolean的二维dp[i][j]记录字符串i,j是否是回文串:
a.j = i时,字符串长度为1,必然是回文串;
b.j = i + 1,字符串长度为2,是回文串的条件是s.charAt(i) == s.charAt(j);
c.j > i + 1,字符串长度大于2,是回文串的条件是在s.charAt(i) == s.charAt(j)的前提下,i+1到到j-1位置的子字符串也是回文串;
2.使用双指针i,j,分别记录第一个和第二个回文串的结束位置
a.第一个回文串起始位置为0,结束位置为i;
b.第二个回文串起始位置为 i + 1,结束位置为j;
c.第三个回文串起始位置为 j + 1, 结束位置为 字符串长度 - 1;
class Solution {
public boolean checkPartitioning(String s) {
int len = s.length();
boolean[][] dp = new boolean[len][len];
for(int right = 0; right < len; right++){
dp[right][right] = true;
for(int left = right - 1; left >= 0; left--){
dp[left][right] = (left == right - 1 ? true : dp[left + 1][right - 1]) && s.charAt(left) == s.charAt(right);
}
}
for(int i = 0; i < len - 2; i++){
for(int j = i + 1; j < len - 1; j++){
if(dp[0][i] && dp[i + 1][j] && dp[j + 1][len - 1]){
return true;
}
}
}
return false;
}
}
时间复杂度O(n^2),空间复杂度O(n^2)。