每日一题做题记录,参考官方和三叶的题解 |
简单题亘古不变的模拟。
class Solution {
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> res = new ArrayList<>();
out:for(int i = left; i <= right; i++) {
int cur = i;
while(cur != 0) {
int t = cur % 10;
if(t == 0 || i % t != 0) //某一位为0 or 不能被整除
continue out;
cur /= 10;
}
res.add(i);
}
return res;
}
}
out
没见过,搜了一下发现是个标签,用来跳出循环,通常break
、continue
啥的都只能跳出当前循环,那加个out
就可以跳出被其标记的那个循环;C++里面是没有java那个标签功能的,那想到三个替代思路:
goto
跳到指定位置(直接放弃、问就是因为爱情);ok
);return
出去。【用这个写一下】class Solution {
public:
vector<int> selfDividingNumbers(int left, int right) {
vector<int> res;
for(int i = left; i <= right; i++)
if(isDN(i))
res.emplace_back(i);
return res;
}
//for循环写成函数
bool isDN(int i) {
int cur = i;
while(cur != 0) {
int t = cur % 10;
if(t == 0 || i % t != 0) //某一位为0 or 不能被整除
return false;
cur /= 10;
}
return true;
}
};
可以一行解决,所以写一下,时空复杂度应该是同上了……吧
class Solution:
def selfDividingNumbers(self, left: int, right: int) -> List[int]:
return [num for num in range(left, right + 1)
if all(i > 0 and num % i == 0
for i in map(int, str(num)))]
数据范围不大( [ 1 , 1 0 4 ] [1,10^4] [1,104]),所以可以先把所有自除数都找出来,然后再根据题目要求圈定范围。
采用二分找到范围内最小的自除数,然后依次输出就可了。
【前面搞static是因为leetcode的判定机制,其实都写函数里面就ok,这样提交的时候就不会每个example都算一遍,提升判定效率,三叶姐的static大法。】
class Solution {
//提前找到数据范围内所有的自除数
static List<Integer> list = new ArrayList<>();
static {
out:for(int i = 1; i < 10000; i++) {
int cur = i;
while(cur != 0) {
int t = cur % 10;
if(t == 0 || i % t != 0)
continue out;
cur /= 10;
}
list.add(i);
}
}
//二分找在题目范围内最小的
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> res = new ArrayList<>();
int l = 0, r = list.size() - 1;
while (l < r) {
int m = l + r >> 1;
if(list.get(m) >= left)
r = m;
else
l = m + 1;
}
while(list.get(r) <= right)
res.add(list.get(r++));
return res;
}
}
用刚才的2.写,设置一个stop
的flag。
然后,没想到C++怎么搞类似上面那种自动执行的static函数,就直接写里面了。
class Solution {
public:
vector<int> selfDividingNumbers(int left, int right) {
//提前找到数据范围内所有的自除数
vector<int> list;
for(int i = 1; i < 10000; i++) {
int cur = i;
bool stop = false;
while(cur != 0 && !stop) {
int t = cur % 10;
if(t == 0 || i % t != 0) //某一位为0 or 不能被整除
stop = true;
cur /= 10;
}
if(!stop)
list.emplace_back(i);
}
//二分找在题目范围内最小的
vector<int> res;
int l = 0, r = list.size() - 1;
while(l < r) {
int m = l + r >> 1;
if(list.at(m) >= left)
r = m;
else
l = m + 1;
}
while(list.at(r) <= right)
res.emplace_back(list.at(r++));
return res;
}
};
list[i]
和用list.at(i)
的区别,简单讲后者更安全,它会自动检查 i i i是否在合法范围内,不会产生越界等问题。避免二分,把索引也预处理出来(不会改变空间复杂度)。
i d x [ i ] idx[i] idx[i]:表示不超过 i i i的最大自除数在 l i s t list list中的编号。
【判定不超过 l e f t left left的最大自除数是否等于自己,等就恰好从此开始,不等就跳到下一个开始】
class Solution {
//提前找到数据范围内所有的自除数
static List<Integer> list = new ArrayList<>();
static int[] idx = new int[100001]; //list编号
static {
for(int i = 1; i < 10000; i++) {
int cur = i;
boolean ok = true;
while(cur != 0 && ok) {
int t = cur % 10;
if(t == 0 || i % t != 0) //某一位为0 or 不能被整除
ok = false;
cur /= 10;
}
if(ok)
list.add(i);
idx[i] = list.size() - 1; //当前情况下最大的自除数编号
}
}
//找在题目范围内最小的
public List<Integer> selfDividingNumbers(int left, int right) {
List<Integer> res = new ArrayList<>();
int i = list.get(idx[left]) == left ? idx[left] : idx[left] + 1; //判断从left还是下一个开始
while(list.get(i) <= right)
res.add(list.get(i++));
return res;
}
}
class Solution {
public:
vector<int> selfDividingNumbers(int left, int right) {
//提前找到数据范围内所有的自除数
vector<int> list;
int idx[100001]; //list编号
for(int i = 1; i < 10000; i++) {
int cur = i;
bool stop = false;
while(cur != 0 && !stop) {
int t = cur % 10;
if(t == 0 || i % t != 0) //某一位为0 or 不能被整除
stop = true;
cur /= 10;
}
if(!stop)
list.emplace_back(i);
idx[i] = list.size() - 1; //当前情况下最大的自除数编号
}
//找在题目范围内最小的
vector<int> res;
int i = list.at(idx[left]) == left ? idx[left] : idx[left] + 1; //判断从left还是下一个开始
while(list.at(i) <= right)
res.emplace_back(list.at(i++));
return res;
}
};
简单题本来模拟就结了,基于数据范围和判定模式,还引申出来一种二分和哈希表避免二分的方法,挺有意思的。
【三月徽章get√,四月加油】
欢迎指正与讨论! |