438. 找到字符串中所有字母异位词
2021.11.28 每日一题
题目描述
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。
示例 2:
输入: s = “abab”, p = “ab”
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的异位词。
提示:
1 <= s.length, p.length <= 3 * 10^4
s 和 p 仅包含小写字母
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
一个简单的滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
//滑动窗口,想想能避免比较26个字母吗,弄个字符串表示吗
int[] ss = new int[26];
for(char c : p.toCharArray()){
ss[c - 'a']++;
}
List<Integer> res = new ArrayList<>();
int[] temp = new int[26];
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
temp[c - 'a']++;
if(i >= p.length() - 1){
for(int j = 0; j < 26; j++){
if(temp[j] != ss[j])
break;
if(j == 25)
res.add(i - p.length() + 1);
}
temp[s.charAt(i - p.length() + 1) - 'a']--;
}
}
return res;
}
}
用一个表示几个位置不同的变量differ来进行O1的判断
或者可以用变长的滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
//滑动窗口,想想能避免比较26个字母吗
//用一个变量表示区别
List<Integer> res = new ArrayList<>();
if(s.length() < p.length())
return res;
int[] ss = new int[26];
for(int i = 0; i < p.length(); i++){
ss[s.charAt(i) - 'a']++;
ss[p.charAt(i) - 'a']--;
}
int differ = 0;
for(int i = 0; i < 26; i++){
if(ss[i] != 0)
differ++;
}
if(differ == 0)
res.add(0);
for(int i = p.length(); i < s.length(); i++){
char a = s.charAt(i - p.length());
char b = s.charAt(i);
//到这里想想,如果向右移动窗口,那么就是增加一个字母,应该是加
//而从左边移出去的字母,因为前面是加,所以这里是减
ss[a - 'a']--;
if(ss[a - 'a'] == 0)
differ--;
else if(ss[a - 'a'] == -1)
differ++;
ss[b - 'a']++;
if(ss[b - 'a'] == 0)
differ--;
else if(ss[b - 'a'] == 1)
differ++;
if(differ == 0)
res.add(i - p.length() + 1);
}
return res;
}
}
786. 第 K 个最小的素数分数
2021.11.29 每日一题
题目描述
给你一个按递增顺序排序的数组 arr 和一个整数 k 。数组 arr 由 1 和若干 素数 组成,且其中所有整数互不相同。
对于每对满足 0 < i < j < arr.length 的 i 和 j ,可以得到分数 arr[i] / arr[j] 。
那么第 k 个最小的分数是多少呢? 以长度为 2 的整数数组返回你的答案, 这里 answer[0] == arr[i] 且 answer[1] == arr[j] 。
示例 1:
输入:arr = [1,2,3,5], k = 3
输出:[2,5]
解释:已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3
很明显第三个最小的分数是 2/5
示例 2:
输入:arr = [1,7], k = 1
输出:[1,7]
提示:
2 <= arr.length <= 1000
1 <= arr[i] <= 3 * 10^4
arr[0] == 1
arr[i] 是一个 素数 ,i > 0
arr 中的所有数字 互不相同 ,且按 严格递增 排序
1 <= k <= arr.length * (arr.length - 1) / 2
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/k-th-smallest-prime-fraction
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
暴力解法,优先队列,要是这样就结束了也不配困难题了
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
int n = arr.length;
PriorityQueue<double[]> pq = new PriorityQueue<>((a, b) -> {
if(b[0] >= a[0])
return 1;
else
return -1;
});
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
pq.offer(new double[]{(double)arr[i] / arr[j], arr[i], arr[j]});
if(pq.size() > k)
pq.poll();
}
}
return new int[]{(int)pq.peek()[1], (int)pq.peek()[2]};
}
}
将每个分母对应的所有数字看成一个链表,然后相当于合并n个有序链表
每次用优先队列取出所有链表的最小值,取到第k个就是答案
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
//暴力很显然不符合要求
//看了答案感觉自己为啥没有想到呢
//对于每一个数组中的数,除了1,都可以作为分母,然后其他比它小的数都可以作为该分母的分子
//那么,每一个分母最小的数肯定是 arr[0] / arr[i];每一个分母对应一个序列
//那么问题转化成了找到这些序列中第k大的数
//首先将每个序列的头部,也就是这n - 1个数放入优先队列中排序
//将其中最小的数取出,然后放入该序列的下一个数,也就是arr[j + 1] / arr[i]
//然后当取出k个以后,就是第k个数
//这个可以保证是最小吗,因为每次取出的肯定是整个序列的最小值,所以可以保证
int n = arr.length;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (arr[a[0]] * arr[b[1]] - arr[a[1]] * arr[b[0]]));
//首先放入所以列表的第一位数
for(int i = 1; i < n; i++){
pq.offer(new int[]{0, i});
}
//然后每次取出所有数的最小值,然后放入该列表的第二个数
for(int i = 1; i < k; i++){
int[] top = pq.poll();
int a = top[0];
int b = top[1];
//System.out.println(a + "," + b);
//如果该列表有下一个数
if(a + 1 < b){
pq.offer(new int[]{a + 1, b});
}
}
int[] top = pq.poll();
return new int[]{arr[top[0]], arr[top[1]]};
}
}
二分+双指针,这个挺难想的
首先得想到用双指针可以计算出小于一个数的分数个数
然后才能自然而然想到二分
而双指针行的通的原因是,当分母增大时,分数肯定减小,所以之前分母小于mid的分子个数对于当前分母依然满足条件,因而可以继续增大分子
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
//二分加双指针也写一下
//思路就是在0到1中找一个数x,使得小于等于x的数恰好有k个
int n = arr.length;
double left = 0.0;
double right = 1.0;
while(true){
double mid = (right + left) / 2;
//计算比mid小的分数个数,用双指针的方法
int i = -1; //左边指针
int count = 0;
int x = 0;
int y = 1;
for(int j = 1; j < n; j++){
while((double)arr[i + 1] / arr[j] < mid){
i++;
}
//找到比mid小的最大数
if(i != -1 && arr[i] * y > arr[j] * x){
x = arr[i];
y = arr[j];
}
count += i + 1;
}
if(count == k)
return new int[]{x, y};
if(count < k){
left = mid;
}else{
right = mid;
}
}
}
}
400. 第 N 位数字
2021.11.30 每日一题,又是一个月的徽章,11月也过去了
题目描述
给你一个整数 n ,请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …] 中找出并返回第 n 位数字。
示例 1:
输入:n = 3
输出:3
示例 2:
输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 里是 0 ,它是 10 的一部分。
提示:
1 <= n <= 2^31 - 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/nth-digit
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
先找n在当前哪个数字范围内,然后精确找到对应的数字,在找对应的位
因为所有数字都共用一个范围,所以用打表的方式把这个范围进行预处理
class Solution {
static List<long[]> list;
static{
list = new ArrayList<>();
int t = Integer.MAX_VALUE;
long range = 10;
long count = 9; //当前数量
int single = 2;
list.add(new long[]{0, 1});
list.add(new long[]{count, single});
while(t >= count){
single++;
count += (range * 10 - range) * (single - 1);
list.add(new long[]{count, single});
range *= 10;
}
}
public int findNthDigit(int n) {
//就查在1 10 100 1000...哪个范围内,然后再查在这个范围内的哪个数
//然后取出这个数的第几位
//在list中查找n的位置
//for(int i = 0; i < list.size(); i++){
// System.out.println(list.get(i)[0] + "," + list.get(i)[1]);
//}
int left = 0;
int right = list.size() - 1;
while(left < right){
int mid = (right - left + 1) / 2 + left;
//要找的是小于等于n的第一个数
//所以如果小于等于n,那么就留下
if(list.get(mid)[0] <= n){
left = mid;
}else{
right = mid - 1;
}
}
//找到了left
int count = (int)list.get(left)[0];
int wei = (int)list.get(left)[1];
int differ = n - count; //总共差了多少位
int idx = differ / wei; //在第几个数
int remainer = differ % wei;
int base = (int)Math.pow(10, wei - 1);
if(remainer == 0){
int num = base + idx - 1;
return num % 10;
}else{
int num = base + idx;
differ = differ - idx * (wei);
//在num中找第differ位
int cha = wei - differ;
return num / (int)Math.pow(10, cha) % 10;
}
}
}