周日的比赛的时候正在外面办事,没有参加。赛后看了下题目,几道题除了表面要考的内容,还是有些能发散扩展的地方。

做题目不是最终目的,通过做题发现知识盲区,去研究学习,才能不断提高。

理论和实际是有关系的,一些题目也都有现实意义。计算机的一些模拟操作,通过数学算法,能够大大减轻代码量和算法复杂度。

第一题是机器人在坐标系上直走和转弯,通过简单的模拟就能实现。但是仔细思考发现还能通过线性代数,坐标变换的方式做,这样在实际中计算更快。甚至还可以用复数来做。

实际扫地机器人可能就用到了类似的算法。让他能够不至于始终原地打转。

第四题是典型的后缀树、后缀数组应用,找字符串最长重复子串。在搜索引擎,或DNA检测中,都是有实际使用场景的。在70年代就已经有应用了,是一个很经典的算法。而且在90年代至今,一直有科学家提升创建后缀树和后缀数组的时间复杂度。这个算法也是在不断发展的。而且在2016年中国的李志泽,李建和霍红卫三位科学家提出了线性时间复杂度,常数空间的最优构造算法。是中国人对算法的贡献。

下面是详细的题解和思考。


比赛的地址 Weekly Contest 136

https://leetcode-cn.com/contest/weekly-contest-136

困于环中的机器人

题目:

困于环中的机器人(Robot Bounded In Circle)

地址:

https://leetcode-cn.com/contest/weekly-contest-136/problems/robot-bounded-in-circle/

题意:

在无限的平面上,机器人最初位于 (0, 0) 处,面朝北方。机器人可以接受下列三条指令之一:

"G":直走 1 个单位

"L":左转 90 度

"R":右转 90 度

机器人按顺序执行指令 instructions,并一直重复它们。

只有在平面中存在环使得机器人永远无法离开时,返回 true。否则,返回 false。

思路:

假设机器人重复走N次指令后,面朝北:

此时如果坐标在原点,则N次循环后就会重复从前的路径。

如果坐标不在原点,此时把当前位置当作原点,就会每N次移动远离一段和当前原点的距离。距离最初的(0,0)位置越来越远。就不存在循环会最原始原点的问题。

其实至多经过四次,机器人就会面朝北。

经过一次指令后,机器人面朝西或东,相当于逆时针或顺时针转了90度,则再经过三次,就面朝北了。

经过一次指令后,朝南则转了180度,共移动两次指令后朝北。

数学方法:

还可以把指令集先计算一遍,得出经过一个指令集后的相对移动位置和方向转角。用矩阵计算,就不用每次都运行一大堆指令模拟,加快运算速度;

还可以用复数来运算,复数对于转90度有简单的运算方法。

class Solution {
public:
bool isRobotBounded(string instructions) {
int x = 0;
int y = 0;
int i = 0;
int dir[][2] = {{0,1},{1,0},{0,-1},{-1,0}};
do
{
for(auto ch : instructions)
{
if(ch=='G')
{
x+=dir[i][0];
y+=dir[i][1];
}
else if(ch=='R')
{
++i;
i%=4;
}
else
{
i+=4;
i--;
i%=4;
}
}
}while(i!=0);
if(x==0&&y==0)
return true;
return false;
}
};

不邻接植花

题目:

不邻接植花(Flower Planting With No Adjacent)

地址:

https://leetcode-cn.com/contest/weekly-contest-136/problems/flower-planting-with-no-adjacent/

题意:

在一个无向图中,每个点的出度都不超过3。有四种颜色,给每个点着色,要求有边相连的点颜色不同。

给出着色方案。

思路:

由于每个点出度不超过3,四个颜色,肯定可以有解。暴力枚举即可。由于图的点很多,边少。在寻找和点相连的点时,不要按点遍历,要按边遍历,否则会超时。

代码:

class Solution {
public:
map<int, map<int, int>> mr;
vector<int> res;
int dfs(int index, int N)
{
if(index > N)
return 0;
for(int color=1;color<=4;++color)
{
res[index-1] = color;
map<int, int> & tmp = mr[index];
bool flag = false;
for(auto it=tmp.begin();it!=tmp.end();++it)
{
if(res[it->first-1]==color)
{
flag = true;
break;
}
}
if(flag == true)
continue;
int ret = dfs(index+1, N);
if(ret == 0)
return 0;
}
return -1;
}
vector<int> gardenNoAdj(int N, vector<vector<int>>& paths) {
res.resize(N, 0);
for(int i=0; i<paths.size(); ++i)
{
int x = paths[i][0];
int y = paths[i][1];
mr[x][y] = 1;
mr[y][x] = 1;
}
dfs(1, N);
return res;
}
};

分隔数组以得到最大和

题目:

分隔数组以得到最大和(Partition Array for Maximum Sum)

地址:

https://leetcode-cn.com/contest/weekly-contest-136/problems/partition-array-for-maximum-sum/

题意:

给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。

返回给定数组完成分隔后的最大和。

思路:

该问题可以划分为子问题求解。

设数组有N个元素A[0]A[1]...A[N-1],sum(i)表示从A[i]~A[N]求解的最大和。

则sum(i) = max( max(A[i]-A[i+m-1])*m + sum(m) ) 其中i<=m<=k;

就是每个从i开始到数组结尾的最大和,等于前m个元素单独划分,再加上剩下元素的最大和。这k中划分方案最大的,就是从i开始到数组结尾最大和最大的。

依次计算到第0个位置结束。

为了计算统一,会用到sum(n),实际没有这个元素,初始化零计算即可。

代码:

class Solution {
public:
int dp[501]={0};
int maxSumAfterPartitioning(vector<int>& A, int K) {
int n = A.size();
for(int i = n-1; i>=0; --i)
{
int ma = 0;
int j;
for(j=i;j<i+K && j < n ;++j)
{
ma = max(ma, A[j]);
dp[i] = max(dp[i], ma*(j - i + 1) + dp[j + 1]);
}
}
return dp[0];
}
};

最长重复子串

题目:

最长重复子串(Longest Duplicate Substring)

地址:

https://leetcode-cn.com/contest/weekly-contest-136/problems/longest-duplicate-substring/

题意:

给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。

返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 ""。)

思路:

后缀数组教科书般的例题。

后缀数组是后缀树的一种变种,能够节省空间。构造的方法有「倍增算法」,「DC3算法」。

主要思想:

设字符串为S(1-n)由n个字符组成。则字符串有n个相同后缀的子串。分别为s(1-n),s(2-n),...,s(n-n)。

然后构建一个SA数组,每个数组存储这些后缀的子串,存储后进行字典序排序。

最后构造出一个height数组,表示SA数组每个元素和前一个元素相同前缀的字符个数。

那么,最长重复子串的长度就是height数组的最大值。

因为最长重复子串一定是两个不同后缀的公共前缀,而且这两个不同后缀的字典序排列后一定是相连的。否则一定有比他更长的。

所以height的最大值能够找到那两个后缀,然后提取公共前缀就找到答案。

代码:

namespace SA
{
bool cmp(int *r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int str[], int sa[], int rank[], int height[], int n, int m)
{
n++;
int i, j, p, *x = t1, *y = t2;
for (i = 0; i < m; i++)
c[i] = 0;
for (i = 0; i < n; i++)
c[x[i] = str[i]]++;
for (i = 1; i < m; i++)
c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1)
{
p = 0;
for (i = n - j; i < n; i++)
y[p++] = i;
for (i = 0; i < n; i++)
if (sa[i] >= j)
y[p++] = sa[i] - j;
for (i = 0; i < m; i++)
c[i] = 0;
for (i = 0; i < n; i++)
c[x[y[i]]]++;
for (i = 1; i < m; i++)
c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--)
sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
if (p >= n)
break;
m = p;
}
int k = 0;
n--;
for (i = 0; i <= n; i++)
rank[sa[i]] = i;
for (i = 0; i < n; i++)
{
if (k)
k--;
j = sa[rank[i] - 1];
while (str[i + k] == str[j + k])
k++;
height[rank[i]] = k;
}
}
int num_rank[MAXN], height[MAXN];
int num[MAXN];
int sa[MAXN];
} // namespace SA class Solution
{
public:
string longestDupSubstring(string S)
{
using namespace SA;
int pos = 0;
int len = 0;
int n = S.length();
for (int i = 0; i <= n; ++i)
{
num[i] = S[i]&0x3f;
}
num[n] = 0;
da(num, sa, num_rank, height, n, 256);
for (int i = 2; i <= n; ++i)
{
if (height[i] > len)
{
pos = sa[i];
len = height[i];
}
}
return S.substr(pos, len);
}
};

Leetcode 第136场周赛解题报告的更多相关文章

  1. Leetcode 第133场周赛解题报告

    今天参加了leetcode的周赛,算法比赛,要求速度比较快.有思路就立马启动,不会纠结是否有更好的方法或代码可读性.只要在算法复杂度数量级内,基本上是怎么实现快速就怎么来了. 比赛时先看的第二题,一看 ...

  2. Leetcode 第137场周赛解题报告

    今天的比赛的题目相对来说比较「直白」,不像前几周都是一些特定的算法,如果你没学过不可能想出来. 做了这些周,对leetcode比赛的题目也发现了一些「规律」. 一般前两道题都很「简单」,只要有想法,直 ...

  3. Leetcode 第135场周赛解题报告

    这周比赛的题目很有特点.几道题都需要找到一定的技巧才能巧妙解决,和以往靠数据结构的题目不太一样. 就是如果懂原理,代码会很简单,如果暴力做,也能做出来,但是十分容易出错. 第四题还挺难想的,想了好久才 ...

  4. 【LeetCode】136. Single Number 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 异或 字典 日期 [LeetCode] 题目地址:h ...

  5. LeetCode 2 Add Two Sum 解题报告

    LeetCode 2 Add Two Sum 解题报告 LeetCode第二题 Add Two Sum 首先我们看题目要求: You are given two linked lists repres ...

  6. LeetCode 第 165 场周赛

    LeetCode 第 165 场周赛 5275. 找出井字棋的获胜者 5276. 不浪费原料的汉堡制作方案 5277. 统计全为 1 的正方形子矩阵 5278. 分割回文串 III C 暴力做的,只能 ...

  7. Leetcode第 217 场周赛(思维量比较大)

    Leetcode第 217 场周赛 比赛链接:点这里 做完前两题我就知道今天的竞赛我已经结束了 这场比赛思维量还是比较大的. 1673. 找出最具竞争力的子序列 题目 给你一个整数数组 nums 和一 ...

  8. 【LeetCode】376. Wiggle Subsequence 解题报告(Python)

    [LeetCode]376. Wiggle Subsequence 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.c ...

  9. 【LeetCode】649. Dota2 Senate 解题报告(Python)

    [LeetCode]649. Dota2 Senate 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...

随机推荐

  1. 图文了解 Kafka 的副本复制机制

    让分布式系统的操作变得简单,在某种程度上是一种艺术,通常这种实现都是从大量的实践中总结得到的.Apache Kafka 的受欢迎程度在很大程度上归功于其设计和操作简单性.随着社区添加更多功能,开发者们 ...

  2. 英语发音规则---字母组合ou的发音规律

    英语发音规则---字母组合ou的发音规律 一.总结 一句话总结: 1.先练习一下题,单词enough划线部分与下列那个单词划线部分读音相同:A. touch  B. mouth  C. soul  D ...

  3. spring自动扫描装配bean

    applicationContext.xml: <!-- 自动扫描service包,根据包中注解自动装配bean --> <context:component-scan base-p ...

  4. C++(二)— STL容器的基本用法

    1.vector基本操作 关于vector简单的讲就是一个动态增长的数组,里面有一个指针指向一片连续的内存空间,当空间装不下的时候会自动申请一片更大的空间(空间配置器)将原来的数据拷贝到新的空间,然后 ...

  5. PHP不用第三变量交换2个变量的值的解决方法

    以前做过一道php面试题是这样的:不使用第三个变量实现交换两个变量的值.一般都是借助第三个中间变量来实现原来两个变量的值交换,但是这道题却要求不能使用中间变量,这对于初学者来说也算是一个难题了.网上找 ...

  6. 04 - Django应用第一步

    知识点 1) 创建项目命令 以及项目结构介绍 2) 创建应用程序命令 应用, 项目的区别 以及应用程序结构 3) 启动项目命令 4) URLs的编写 include()的使用 get发送参数的格式 u ...

  7. 洛谷 P1187 3D模型

    题目描述 一座城市建立在规则的n×m网格上,并且网格均由1×1正方形构成.在每个网格上都可以有一个建筑,建筑由若干个1×1×1的立方体搭建而成(也就是所有建筑的底部都在同一平面上的).几个典型的城市模 ...

  8. VC6++常用快捷键

    VC6快捷键大全(转载) VC6快捷键大全,记在这里,方便查阅.F1: 帮助Ctrl+O :OpenCtrl+P :PrintCtrl+N :NewCtrl+Shift+F2 :清除所有书签F2 :上 ...

  9. chrome中的content script脚本文件

    打开chrome的devtools工具,sources下有一个Content script: 1 chrome插件开发过程中难免会遇到使用content script来操作页面的dom,在chrome ...

  10. 人物-IT-马云:马云

    ylbtech-人物-IT-马云:马云 马云 (阿里巴巴集团创始人) 马云,男,汉族,中共党员,1964年9月10日生于浙江省杭州市,祖籍浙江省嵊州市谷来镇, 阿里巴巴集团主要创始人,现担任阿里巴巴集 ...