本人技术小白一枚,文章只是记录个人的解题思路和过程
牛客网地址:原题链接
为了不断优化推荐效果,今日头条每天要存储和处理海量数据。假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值,我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为k。因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间(不存在L1<=L2<=R2<=R1)。
输入: 第1行为n代表用户的个数 第2行为n个整数,第i个代表用户标号为i的用户对某类文章的喜好度 第3行为一个正整数q代表查询的组数 第4行到第(3+q)行,每行包含3个整数l,r,k代表一组查询,即标号为l<=i<=r的用户中对这类文章喜好值为k的用户的个数。 数据范围n <= 300000,q<=300000 k是整型
输出:一共q行,每行一个整数代表喜好值为k的用户的个数
5
1 2 3 3 5
3
1 2 1
2 4 5
3 5 3
1
0
2
一看题目,最先想到的就是枚举遍历:
将所有的用户喜好度k值保存在数组arr[]中,然后遍历查询的区间[l , r],判断是否喜好值是否是k。
不过看一下数据范围:n<=300000,q<=300000,直接遍历的方法时间复杂度是 O(nq),很明显是无法通过所有测试用例的。
这个时候我们必须换一种思路了,再分析一下题目,我们可以发现,除了可以使用用户序列号来获取喜好值k,我们同样可以通过喜好值k来反向获取用户的序列号列表,即将k作为key值,value为用户序列号列表List;key-value的存储方式,很明显,选用的集合类是HashMap;
想明白这一点,这道题就不难了,直接通过查询的k值来判断是否存在用户集合List,并遍历集合List,在[l, r]区间内即符合条件。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
}
Map<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < n; i++) {
int key = a[i];
if (map.containsKey(key)) {
List<Integer> list = map.get(key);
list.add(i+1);
}else {
List<Integer> list = new ArrayList<>();
list.add(i+1);
map.put(key, list);
}
}
int q = scanner.nextInt();
while((q--) > 0) {
int l = scanner.nextInt();
int r = scanner.nextInt();
int k = scanner.nextInt();
int count = 0;
List<Integer> list = map.get(k);
if (list != null) {
for(Integer integer : list) {
if (integer >= l && integer <= r) {
count++;
}
}
}
System.out.println(count);
}
}
}
}
作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。
第一行输入n,m,c三个数,用空格隔开。(1 <= n <= 10000, 1 <= m <= 1000, 1 <= c <= 50) 接下来n行每行的第一个数num_i(0 <= num_i <= c)表示第i颗珠子有多少种颜色。接下来依次读入num_i个数字,每个数字x表示第i颗柱子上包含第x种颜色(1 <= x <= c)
一个非负整数,表示该手链上有多少种颜色不符需求。
5 2 3
3 1 2 3
0
2 2 3
1 2
1 3
2
这道题同样可以使用和第一题一样的方法,用颜色c做key值,value是串珠标号列表List;
m个连续串珠内不能一种颜色出现两次,记录不合格的颜色数量,就变成了获取每种颜色的串珠标号列表List,判断List中相邻的两个数之间的差值是否大于m。
需要注意的是,因为珠串是一个环状结构,所以List中最后一个元素需要单独判断。
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
int n = scanner.nextInt();
int m = scanner.nextInt();
int c = scanner.nextInt();
Map<Integer, List<Integer>> map = new HashMap<>();
for(int i=0; i<n; i++) {
int num = scanner.nextInt();
for(int j=0; j<num; j++) {
int key = scanner.nextInt();
if (map.containsKey(key)) {
List<Integer> list = map.get(key);
list.add(i);
}else {
List<Integer> list = new ArrayList<>();
list.add(i);
map.put(key, list);
}
}
}
int count = 0;
Boolean flag;
for (int i = 1; i <= c; i++) {
flag = false;
List<Integer> list = map.get(i);
if (list != null) {
Collections.sort(list);
int j;
for (j = 0; j < list.size()-1; j++) {
int left = list.get(j);
int right = list.get(j+1);
if (left + m > right) {
count++;
flag = true;
break;
}
}
if (!flag && (j > 0) && ((list.get(j) + m) % n) > list.get(0)) {
count++;
}
}
}
System.out.println(count);
}
}
}
字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?
第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)
一个非负整数,表示操作之后,连续最长的相同字母数量。
abcbaa 2
2
要求移动后形成最长连续相同字母子串,这个最长连续子串可能是a或b……z中的任意一个字母,很明显,可以把每个字母单独拆分出来,作为一个整体考虑,最后的结果比较每个字母得到的最长子串的长度,选取最大的即可。
首先就是遍历这个字符串的所有字符,使用字母的顺序值作为key值,把同一字母的位置,保存在一个List中,List作为一个序列从小到大排列。
接下来就是求解连续最长子序列的问题了,并且操作的次数还不能超过m,因此我们可以使用动态规划,先从小到大枚举段长,依次求得该段长的所有子序列的操作次数,并判断是否小于等于m,如果满足要求,就更新答案。
注:这道题的思路是我参考别的大神的
原文地址:https://blog.csdn.net/flushhip/article/details/79416715
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
String string = scanner.next();
int m = scanner.nextInt();
char[] cs = string.toCharArray();
Map<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < cs.length; i++) {
int key = cs[i] - 'a';
if (map.containsKey(key)) {
List<Integer> list = map.get(key);
list.add(i);
}else {
List<Integer> list = new ArrayList<>();
list.add(i);
map.put(key, list);
}
}
int count = 0;
for(int i=0;i<26;i++) {
List<Integer> list = map.get(i);
if (list != null) {
int[][] dp = new int[list.size()][list.size()];
//len指连续子串长度
for(int len=2;len<=list.size();len++) {
//j指子串起始位置,(j + len -1)指子串结束位置,
//dp[j][j+len-1]指在j~(j+len-1)这段子串移动成连续子串所需要的次数
for (int j = 0; j + len - 1 < list.size(); j ++) {
/*dp[j+1][j+len-2]指将(j+1)~(j+len-2)之间的目标字母移动到一起,
这个移动次数就是dp[i + 1][i + len - 2];
然后将两个端点的字母向中间移,移动的距离就为
(list.get(j+len-1) - list.get(j)) - (len-1)
*/
dp[j][j+len-1] = dp[j+1][j+len-2] + list.get(j+len-1) - list.get(j) + 1 -len;
if (dp[j][j+len-1] <= m && count < len) {
count = len;
}
}
}
}
}
System.out.println(count);
}
}
}