刚完成周赛,趁着思路还热乎,来写一篇题解。
总的来说,这次的题目难度还好,难度集中体现在三四题上。第三题需要砸不少代码,第四题需要对前缀数组知识的了解。
咱们一道一道说:
题目
首先,第一题。题意很清晰,题目中给定两个数组,遍历两个数组,按照 index 数组的下标插入 num 数组中的元素。
这个题想要考察的点是线性结构的插入元素。所以我们选用链表来完成,鉴于 Java 中自带链表,这道题就成了模板的使用。
代码:
class Solution {
public int[] createTargetArray(int[] nums, int[] index) {
LinkedList<Integer> list = new LinkedList();
int n = nums.length;
for(int i = 0 ;i < n;i++)
{
list.add(index[i], nums[i]);
}
int[] ans = new int[n];
for(int i = 0;i < n;i++)
{
ans[i] = list.get(i);
}
return ans;
}
}
题目
这个题目,题意也非常的清晰,找出数组中因数个数为四个的元素,并计算这些因数的和。
遍历数组很简单,分解因数并统计个数也很简单,但有一个问题在于它的数据范围:
1 <= nums.length <= 10^4
1 <= nums[i] <= 10^5
这个数据范围是不允许我们暴力的循环枚举因子分解每一个元素的,那样的复杂度是O(NM)已经是 10^9 了,基本上可以宣告TLE。
所以需要在枚举因子上面做一些优化:每次枚举到其平方根即可。因为不难证明,因数的分布一定在一个数的平方根的两侧。
最后需要注意这个数是否是个平方数,如果是平方数,可以断言这个数的因数个数为奇数个,进而不可能为四个,可以省去后面的统计。
代码:
class Solution {
private List<Integer> split(int k)
{
LinkedList<Integer> list = new LinkedList();
if((int)Math.sqrt(k) * (int)Math.sqrt(k) == k)
{
list.add((int)Math.sqrt(k));
return list;
}
for(int i = 1;i < Math.sqrt(k);i++)
{
if(k % i == 0)
{
list.add(i);
list.add(k / i);
}
if(list.size() > 4)
{
return list;
}
}
return list;
}
public int sumFourDivisors(int[] nums) {
LinkedList<Integer> list = new LinkedList();
int ans = 0;
for(int k:nums)
{
list = (LinkedList<Integer>) split(k);
if(list.size() == 4)
{
for(Integer i : list)
{
ans += i;
}
}
}
return ans;
}
}
题目
这个题很有意思,思路也不难,但就是不太好写。(也可能是我比较笨)
网格上的问题,往往可以抽象成图的问题,而判断连通性的问题,往往都伴随着 bfs 的使用,这个题就是个很好的例子。上一个类似的例子,可以参考周赛178的T4。(不了解图或BFS的小伙伴可以参考这里)
不过,对于这个题来说,其实没有必要建图,直接 bfs 就好。可是问题就出在这个题目的规则是在是不太友好,连通的条件是两个方块需要对上,而以往类似的题目只需要单方面可行即可移动。而且每个方块还有两个可移动方向。
在仔细分析六条道路发现其实状态也不多,如果我们写出了12两种道路的上下左右四种情况,那么其实3456这四种道路是12的四选二组合(你品,你细品)。于是,对于这道题,就可以实行面向复制粘贴编程的策略了(滑稽)。
最终步骤:
这个过程看起来很简单 写起来并不容易。在判断是否可以到达下一个格子的时候需要判断:
也就是说,三个条件需要同时满足,每个类型还有两个方向,233.
话不多说,上代码:
class Solution {
public boolean hasValidPath(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[][] queue = new int[n * m + 10][2];
boolean[][] vis = new boolean[n][m];
int l = 1,r = 0;
queue[++r][0] = 0;
queue[r][1] = 0;
vis[0][0] = true;
int i,j;
while(l <= r)
{
i = queue[l][0];
j = queue[r][1];
l++;
if(i == n - 1 && j == m - 1)
{
return true;
}
if(grid[i][j] == 1)
{
if(j > 0 && !vis[i][j - 1] &&
(grid[i][j - 1] == 4 || grid[i][j - 1] == 6 || grid[i][j - 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j - 1;
vis[i][j - 1] = true;
}
if(j < m - 1 && !vis[i][j + 1] &&
(grid[i][j + 1] == 3 || grid[i][j + 1] == 5 || grid[i][j + 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j + 1;
vis[i][j + 1] = true;
}
}
if(grid[i][j] == 2)
{
if(i > 0 && !vis[i - 1][j] &&
(grid[i - 1][j] == 3 || grid[i - 1][j] == 4 || grid[i - 1][j] == 2))
{
queue[++r][0] = i - 1;
queue[r][1] = j;
vis[i - 1][j] = true;
}
if(i < n - 1 && !vis[i + 1][j] &&
(grid[i + 1][j] == 6 || grid[i + 1][j] == 5 || grid[i + 1][j] == 2))
{
queue[++r][0] = i + 1;
queue[r][1] = j;
vis[i + 1][j] = true;
}
}
if(grid[i][j] == 3)
{
if(j > 0 && !vis[i][j - 1] &&
(grid[i][j - 1] == 4 || grid[i][j - 1] == 6 || grid[i][j - 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j - 1;
vis[i][j - 1] = true;
}
if(i < n - 1 && !vis[i + 1][j] &&
(grid[i + 1][j] == 6 || grid[i + 1][j] == 5 || grid[i + 1][j] == 2))
{
queue[++r][0] = i + 1;
queue[r][1] = j;
vis[i + 1][j] = true;
}
}
if(grid[i][j] == 4)
{
if(j < m - 1 && !vis[i][j + 1] &&
(grid[i][j + 1] == 3 || grid[i][j + 1] == 5 || grid[i][j + 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j + 1;
vis[i][j + 1] = true;
}
if(i < n - 1 && !vis[i + 1][j] &&
(grid[i + 1][j] == 6 || grid[i + 1][j] == 5 || grid[i + 1][j] == 2))
{
queue[++r][0] = i + 1;
queue[r][1] = j;
vis[i + 1][j] = true;
}
}
if(grid[i][j] == 5)
{
if(j > 0 && !vis[i][j - 1] &&
(grid[i][j - 1] == 4 || grid[i][j - 1] == 6 || grid[i][j - 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j - 1;
vis[i][j - 1] = true;
}
if(i > 0 && !vis[i - 1][j] &&
(grid[i - 1][j] == 3 || grid[i - 1][j] == 4 || grid[i - 1][j] == 2))
{
queue[++r][0] = i - 1;
queue[r][1] = j;
vis[i - 1][j] = true;
}
}
if(grid[i][j] == 6)
{
if(j < m - 1 && !vis[i][j + 1] &&
(grid[i][j + 1] == 3 || grid[i][j + 1] == 5 || grid[i][j + 1] == 1))
{
queue[++r][0] = i;
queue[r][1] = j + 1;
vis[i][j + 1] = true;
}
if(i > 0 && !vis[i - 1][j] &&
(grid[i - 1][j] == 3 || grid[i - 1][j] == 4 || grid[i - 1][j] == 2))
{
queue[++r][0] = i - 1;
queue[r][1] = j;
vis[i - 1][j] = true;
}
}
}
return false;
}
}
题目
最后一道题,其实没什么可说的。从前缀与后缀相同这一点可以大致判断这是一道前缀函数的模板题,而且数据规模貌似限制了绝大多数的暴力算法。
答案就是前缀数组最后一位的长度,返回subString即可。
虽然这个题抛开暴力算法不太好考虑,但是前缀函数其实写出来很短,而短小代码解决复杂问题的过程也是不好理解和得出的,所以这道题的难度就在于此。有关前缀函数和KMP算法这个就不展开了,感兴趣的小伙伴可以看这里
代码:
class Solution {
public int[] getF(String str)
{
char[] ch = str.toCharArray();
int[] f = new int[str.length()];
for(int i = 1,j = 0;i < str.length();i++)
{
while(j > 0 && ch[j] != ch[i])
{
j = f[j - 1];
}
if(ch[j] == ch[i])
{
j++;
}
f[i] = j;
}
return f;
}
public String longestPrefix(String s) {
int ans = getF(s)[s.length() - 1];
return s.substring(0,ans);
}
}