算法练习- 其他算法练习3

文章目录

  • 数据分类
  • 矩阵稀疏扫描
  • VLAN资源池
  • 子序列长度
  • 代码编辑器
  • 流水线
  • 热点网站统计
  • 相对开音节
  • 需要打开多少监控器

数据分类

  • 对数据a 进行分类,数据a中的4个字节相加,结果对 b 取模,如果得到的结果小于c 则数据a为有效类型,其类型为取模后的值;如果得到的结果大于或者等于c 则数据a为无效类型。

  • 如数据a = 0x01010101,b = 3
    按照分类方法计算:(0x01 + 0x01 + 0x01 + 0x01) % 3 = 1,如果c等于2,则此a就是有效类型,其类型为1
    如果c等于1,则此a是无效类型

输入:
一行输入12个数据,
第一个数据为c,第二个数据为b,
剩余10个数据为需要分类的数据
输出:
有效类型中包含数据最多的类型,
并输出该类型含有多少个数据

示例一
输入:
3 4 256 257 258 259 260 261 262 263 264 265
输出:
3

思路:

  • 依次遍历每个待分类的数,根据计算方法判断其是否有效;
  • 有效则以key-val形式存入哈希,val为列表形式;
  • sorted排序字典,根据val的个数从大到小排序;

python实现:

def add_byte(hex_str):
    # 八个 十六进制数 00 00 01 00
    # 每个字节相加
    n = len(hex_str)
    temp_list = []
    for i in range(0, n, 2):
        temp_list.append(int("0x" + hex_str[i:i+2], 16))
    return sum(temp_list)


def calc_mod(a, b, c):
    # 判断a是否有效

    # a十进制转为十六进制 "0x100"
    hex_str = hex(a)
    hex_str = hex_str[2:]
    if len(hex_str) < 8:
        hex_str = "0" * (8 - len(hex_str)) + hex_str

    # 八位十六进制数  每个字节相加
    temp_result = add_byte(hex_str)

    # 求和结果对b 求模
    mod_val = temp_result % b
    if mod_val < c:
        return str(mod_val)
    else:
        return ""



# 3 4 256 257 258 259 260 261 262 263 264 265
alist = list(map(int, input().strip().split()))
c, b = alist.pop(0), alist.pop(0)

cls_dict = {}
for a in alist:
    mod_val = calc_mod(a, b, c)
    if mod_val: # 类型字符串
        if mod_val in cls_dict:
            cls_dict.get(mod_val).append(a)

        else:
            cls_dict[mod_val] = [a]

# 排序字典
result = sorted(cls_dict.items(), key=lambda i: len(i[1]), reverse=True)

# print(result[0][0])
print(len(result[0][1]))

java实现:

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;


public class Main0010 {
  public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
      int[] ints = new int[12];
      for (int i = 0; i < ints.length; i++) {
        ints[i] = scanner.nextInt();
      }
      solution(ints);
    }
  }

  private static void solution(int[] ints) {
    int c = ints[0];
    int b = ints[1];
    Map<Integer, Integer> map = new HashMap<>();

    for (int i = 2; i < ints.length; i++) {
      int r = intByteSum(ints[i]) % b;
      if (r < c) map.put(r, map.containsKey(r) ? map.get(r) + 1 : 1);
    }

    int max = 0;
    for (Integer value : map.values()) {
      if (value > max) max = value;
    }
    System.out.println(max);
  }

  private static int intByteSum(int x) {
    int sum = 0;
    for (int i = 0; i < 4; i++) {
      sum += (byte) (x >> (i * 8));
    }
    return sum;
  }
}

 

矩阵稀疏扫描

  • 如果矩阵中的许多数都为零,那么该矩阵就是稀疏的。
  • 给定一个矩阵,逐行和逐列地扫描,如果某一行或者某一列内,连续出现0的个数超过了行宽或者列宽的一半 (取整) ,则认为该行或者该列是稀疏的。

输入:
第一行输入 M行 和 N列,表示矩阵的大小 M∗N,0 后续每行 输入 N 个成员,矩阵成员都是有符号整数,范围 -32,768 到 32,767

输出:
一行 输出稀疏行数
一行 输出稀疏列数

示例一
输入:
3 3
1 0 0
0 1 0
0 0 1
输出:
3
3
说明
给定的3 * 3矩阵里,每一行和每一列内都存在2个0,行宽3,列宽3,[3/2] = 1,因此稀疏行有3个,稀疏列有3个。

示例二
输入:
5 3
-1 0 1
0 0 0
-1 0 0
0 -1 0
0 0 0
输出:
4
3

思路:

  • 逐行扫描,获取稀疏行数;
  • 逐列扫描,获取稀疏列数;

python实现:


if __name__ == '__main__':
    row, col = input().strip().split()
    row = int(row)
    col = int(col)

    # 输入row行数据
    matrix = []
    for i in range(row):
        cur_row = list(map(int, input().strip().split()))
        matrix.append(cur_row)

    sparse_row = 0
    sparse_col = 0

    for i in range(row):
        true_val = 0
        for j in range(col):
            if matrix[i][j]:
                true_val += 1
        if true_val < col - col // 2:
            sparse_row += 1

    for j in range(col):
        true_val = 0
        for i in range(row):
            if matrix[i][j]:
                true_val += 1
        if true_val < row - row // 2:
            sparse_col += 1

    print(sparse_row)
    print(sparse_col)

java实现:

 

VLAN资源池

  • Vlan是一种为局域网设备进行逻辑划分的技术
  • 为了标识不同的vlan, 引入了vlan id: 1 ~ 4094之间的整数
  • 定义一个vlan id 的资源池,资源池中连续的vlan用 开始vlan-结束vlan 来表示,不连续的用单个整数表示,所有id以逗号分隔;
  • 现有一个vlan资源池,从池中申请一个vlan,并从池中删除申请的vlan;
  • 输出剩余的vlan资源池;

输入:
第一行- vlan资源池,如 20-21,15,18,30,5-10
第二行- 要申请的vlan,vlan的取值范围1 ~ 4094

输出:
原vlan资源池中移除申请的vlan后的资源池【升序排列 字符串】
如果申请的vlan不在原资源池,输出升序排序的原资源池 【字符串】

示例一
输入:
1-5
2
输出:
1,3-5
说明
原vlan资源池中有1 2 3 4 5 移除2后
剩下的1 3 4 5按照升序排列的方式为 1 3-5

示例二
输入:
20-21,15,18,30,5-10
15
输出:
5-10,18,20-21,30
说明
原vlan资源池中有5 6 7 8 9 10 15 18 20 21 30
移除15后 剩下的为 5 6 7 8 9 10 18 20 21 30
按照题目描述格式并升序后的结果为5-10,18,20-21,30

示例三
输入
5,1-3
10
输出
1-3,5
说明
资源池中有1 2 3 5
申请的资源不在资源池中
将原池升序输出为1-3,5

思路:

  • 将带有-的范围id 转为 单个id;
  • 所有id放入一个整数列表,并升序排序;
  • 删除申请的id
  • 将连续的id再转为带有-的范围id,连同单个的id拼接为字符串,输出。

python:


def restore_vlan_id(resource):
    # 20-21,15,18,30,5-10
    temp = []
    for i in resource:
        if i.isdigit():
            temp.append(int(i))
        else:
            start, end = i.split("-")
            seq = list(range(int(start), int(end)+1, 1))
            temp.extend(seq)
    # id整数列表
    return temp


def remove_vlan(vlan, resource):
    if vlan in resource:
        resource.remove(vlan)
    # 连续的id需要转为 start-end格式
    vlan_result = []
    pre = start = resource[0]
    for i in range(1, len(resource)):
        if resource[i] - pre == 1:
            pre = resource[i]

        else:
            end = pre
            if end == start:
                vlan_result.append(str(end))
            else:
                vlan_result.append(f"{start}-{end}")
            pre = start = resource[i]

    if pre == start:
        vlan_result.append(str(pre))
    else:
        vlan_result.append(f"{start}-{pre}")

    print(",".join(vlan_result))


if __name__ == '__main__':
    # 20-21,15,18,30,5-10
    resource = input().strip().split(",")
    target = int(input().strip())

    # 确保所有的id为单个的id
    resource_ = restore_vlan_id(resource)
    # 排序
    resource_.sort()
    # 删除申请的vlan id
    remove_vlan(target, resource_)

java:

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;


public class Main0040 {
  public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
      String input = scanner.nextLine();
      int request = scanner.nextInt();
      solution(input,request);
    }
  }

  private static void solution(String input, int request) {
    Set<Integer> set = new TreeSet<>();
    for (String str : input.split(",")) {
      if (str.contains("-")) {
        String[] split = str.split("-");
        int start = Integer.parseInt(split[0]);
        int end = Integer.parseInt(split[1]);
        for (int i = start; i <= end; i++) {
          set.add(i);
        }
      } else {
        set.add(Integer.parseInt(str));
      }
    }

    set.remove(request);

    ArrayList<Integer> list = new ArrayList<>(set);
    StringBuilder sb = new StringBuilder();


    Integer start = list.get(0);
    Integer last = start;

    for (int i = 1; i < list.size(); i++) {
      Integer cur = list.get(i);
      if (cur == last + 1) {
        last = cur;
      } else {
        append(sb, start, last);
        start = last = cur;
      }
    }
    append(sb, start, last);

    System.out.println(sb.substring(0, sb.length() - 1));
  }

  private static void append(StringBuilder sb, Integer start, Integer last) {
    if (start.equals(last)) {
      sb.append(last).append(",");
    } else {
      sb.append(start).append("-")
          .append(last).append(",");
    }
  }
}

 

子序列长度

  • 有 n 个正整数组成的一个序列,给定一个整数sum
  • 求最长的连续子序列,使他们的和等于sum,返回子序列的长度
    如果无满足要求的序列 返回-1

输入:
第一行,正整数序列
第二行,整数sum

输出:
满足条件的子序列的长度
如果无满足要求的序列 返回-1

示例一
输入:
1,2,3,4,2
6
输出:
3
说明
1,2,3和4,2两个序列均能满足要求
所以最长的连续序列为1,2,3,因此结果为3

示例二
输入:
1,2,3,4,2
20
输出
-1
说明
没有满足要求的子序列,返回-1

思路:

  • 遍历正整数序列中的每个元素,以每个元素开头,寻找满足条件的连续子序列长度;(双指针、while i < n、求和与sum比较)
  • 连续子序列求和等于sum时,当前子序列长度与当前max_len比较,取最大;

python:


def get_max_len(sum_, seq):
    max_len = -1
    n = len(seq)
    if n == 0:
        print(max_len)
        return
    elif n == 1:
        if seq[0] == sum_:
            max_len = 1
            print(max_len)
            return
        else:
            print(max_len)
            return
    else:
        pre = i = 0
        temp_sum = 0
        while i < n:
            temp_sum += seq[i]
            if temp_sum > sum_:
                temp_sum = 0
                pre += 1
                i = pre
                continue

            elif temp_sum == sum_:
                # 满足求和 相等
                max_len = max(max_len, i - pre + 1)
                pre += 1
                i = pre
                continue
            i += 1

        print(max_len)
        return


if __name__ == '__main__':
    # 1,2,3,4,2
    seq = list(map(int, input().strip().split(",")))
    sum_ = int(input().strip())

    # 获取子序列的最大长度
    get_max_len(sum_, seq)

java:

import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;


public class Main0042 {
  public static void main(String[] args) {

    try (Scanner scanner = new Scanner(System.in)) {

      List<Integer> ints = Arrays.stream(scanner.nextLine().split(","))
          .map(Integer::parseInt)
          .collect(Collectors.toList());

      int sum = Integer.parseInt(scanner.nextLine());

      int max_len = solution(ints, sum);

      System.out.println(max_len);
    }
  }

  private static int solution(List<Integer> ints, int sum) {
    int max_len = 0;

    for (int i = 0; i < ints.size(); i++) {
      int tmp_sum = 0;
      int sub_len = 0;
      for (int j = i; j < ints.size(); j++) {
        if (tmp_sum > sum) {
          break;
        }
        tmp_sum += ints.get(j);
        sub_len++;
        if (tmp_sum == sum && sub_len > max_len) {
          max_len = sub_len;
        }
      }
    }

    max_len = max_len == 0 ? -1 : max_len;
    return max_len;
  }
}

 

代码编辑器

某公司为了更高效的编写代码,邀请你开发一款代码编辑器程序。
程序的输入为 已有的代码文本和指令序列,程序需输出编辑后的最终文本。指针初始位置位于文本的开头。
支持的指令(X为大于等于0的整数, word 为无空格的字符串):

FORWARD X 指针向前(右)移动X,如果指针移动位置超过了文本末尾,则将指针移动到文本末尾
BACKWARD X 指针向后(左)移动X,如果指针移动位置超过了文本开头,则将指针移动到文本开头
SEARCH-FORWARD word 从指针当前位置向前查找 word 并将指针移动到word的起始位置,如果未找到则保持不变
SEARCH-BACKWARD word 在文本中向后查我 word 并将指针移动到word的起始位置,如果未找到则保持不变
INSERT word 在指针当前位置前插入word,并将指针移动到word的结尾
REPLACE word 在指针当前位置替换并插入字符(删除原有字符,并增加新的字符)
DELETE X 在指针位置删除X个字符
输入描述
输入的第一行为命令列表的长度

K
输入的第二行为文件中的原始文本
接下来的

K 行,每行为一个指令

输出描述
编辑后的最终结果

备注
文本最长长度不超过 256K

示例一
输入
1
ello
INSERT h
输出
hello
说明
在文本开头插入

示例二
输入
2
hllo
FORWARD 1
INSERT e
输出
hello
说明
在文本的第一个位置插入

示例三
输入
2
hell
FORWARD 1000
INSERT o
输出
hello
说明
在文本的结尾插入

示例四
输入
1
hello
REPLACE HELLO
输出
HELLO
说明
替换

示例五
输入
1
hello
REPLACE HELLO_WORLD
输出
HELLO_WORLD
说明
超过文本长度替换

示例六
输入
2
hell
FORWARD 10000
REPLACE O
输出
hellO
说明
超出文本长度替换

 

流水线

  • 流水线总是优先并行处理时间最短的作业
  • 现给定流水线个数m,需要完成的作业数n
    每个作业的处理时间分别为 t1,t2…tn
  • 计算处理完所有作业的耗时为多少
    当 n > m时, 流水线首先处理时间最短的m个作业,其他的等待
    当某个作业完成时,依次从剩余作业中取耗时最短的进入流水线处理。

输入:
第一行 输入流水线个数m和作业数 n
第二行 输入每个作业的处理时长 t1,t2…tn
0 < m, n< 100
0 < t1,t2…tn < 100

输出:
输出处理完所有作业的总时长

示例一
输入:
3 5
8 4 3 2 10
输出
13
说明
先安排时间为2,3,4的三个作业
第一条流水线先完成作业
调度剩余时间最短的作业8
第二条流水线完成作业
调度剩余时间最短的作业10
总共耗时 就是二条流水线完成作业时间13(3+10)

思路:

  • 耗时从小到大排序;
  • 哈希存储每个流水线处理任务的总时长
    • time_dict = {0: xx, 1: xx, 2: xxx}
    • 每个作业索引 i d x % m {idx\%m } idx%m对应到流水线 ,对应流水线的耗时就是作业时间的累加
    • 取耗时总数最大的那条流水线的时间。

python:


if __name__ == '__main__':
    # 3 5
    # 8 4 3 2 10
    m, n = list(map(int, input().strip().split()))
    task_time = list(map(int, input().strip().split()))
    task_time.sort()
    # 每条流水线的耗时
    pipeline_time = {i:0 for i in range(m)}
    # 每一个作业都对应到一条流水线
    for i in range(n):
        mod = i % m
        val = pipeline_time.get(mod)
        val += task_time[i]
        pipeline_time[mod] = val

    # 流水线时间排序
    sorted_pipeline_time = sorted(pipeline_time.items(), key=lambda i:i[1], reverse=True)
    print(sorted_pipeline_time[0][1])

java

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;


public class Main0035 {
  public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
      int m = scanner.nextInt();
      int n = scanner.nextInt();
      List<Integer> jobs = new ArrayList<>();
      for (int i = 0; i < n; i++) {
        jobs.add(scanner.nextInt());
      }
      solution(m, n, jobs);
    }
  }

  private static void solution(int m, int n, List<Integer> jobs) {
    jobs = jobs.stream().sorted().collect(Collectors.toList());

    if (n <= m) {
      System.out.println(Collections.max(jobs).intValue());
      return;
    }

    ArrayList<Integer> res = new ArrayList<>();

    for (int i = 0; i < m; i++) {
      res.add(jobs.get(i));
    }

    for (int i = m; i < jobs.size(); i++) {
      Integer min = res.stream().sorted().iterator().next();
      int index = res.indexOf(min);
      res.set(index, res.get(index) + jobs.get(i));
    }

    Integer maxTime = res.stream().max(Integer::compareTo).get();

    System.out.println(maxTime);
  }
}

 

热点网站统计

  • 现要动态统计访问最多的网页URL topN,设计一个算法,可以高效动态统计TopN的页面

输入:
每一行都是一个URL或一个数字
如果是URL代表一段时间内的网页访问
如果是一个数字N 代表本次需要输出的TopN个URL
输入约束:
总访问网页数量小于5000个, 单网页访问次数小于65535次
网页URL仅由字母数字和.分隔符组成,且长度小于等于127字节
数字是正整数,小于等于10 ,且小于当前总访问网页数
输出描述
每行输入对应一行输出
输出按访问次数排序的前N个URL,用逗号分割
输出要求:

每次输出要统计之前所有输入,不仅是本次输入
如果有访问次数相等的URL,按URL的字符串字典序升序排列,输出排序靠前的URL
示例一
输入
news.qq.com
news.sina.com.cn
news.qq.com
news.qq.com
game.163.com
game.163.com
www.huawei.com
www.cctv.com
3
www.huawei.com
www.cctv.com
www.huawei.com
www.cctv.com
www.huawei.com
www.cctv.com
www.huawei.com
www.cctv.com
www.huawei.com
3
输出
news.qq.com,game.163.com,news.sina.com.cn
www.huawei.com,www.cctv.com,news.qq.com
示例二
输入
news.qq.com
www.cctv.com
1
www.huawei.com
www.huawei.com
2
3
输出
news.qq.com
www.huawei.com,news.qq.com
www.huawei.com,news.qq.com,www.cctv.com

思路:

  • 哈希计数每个url;
  • while True 循环输入,遇到数值就输出topN;
  • 排序字典,访问次数相同的,要按照url 字符升序排序(双指针)
  • 最后用“,”拼接 topN url 输出;

 

相对开音节

  • 相对开音节的结构 为辅音+元音(aeiou)+辅音(除r外的辅音),常见的单词有bike cake
  • 给定一个句子,反转每个单词的字母,若单词中包含数字等其他的非字母时,该单词不进行反转
  • 反转后,计算其中含有相对开音节结构的长度为4且以e结尾的子串个数
    (连续子串中部分字符可以重复)

输入:
一个英文句子,长度<10000, 字母只考虑小写
输出:
满足要求的含有相对开音节结构的子串个数

示例一
输入:
ekam a ekac
输出:
2
说明
反转后为make a cake,其中make和cake为相对开音节子串
返回2

示例二
输入:
!ekam a ekekac
输出
2
说明
反转后为 !ekam a cakeke
因为!ekam含有非英文字母,所以未反转
其中 cake和keke 为相对开音节子串 返回2

思路:

  • 句子以空格分割,反转每一个单词的字母,含有非字母的单词不作反转;
  • 依次遍历反转后的单词列表,以单词的每个字符开头,截取四个字符;
    • 第一个字符 是否辅音(除了aeiou的字母)
    • 第二个字符 是否元音
    • 第三个字符是否辅音且非r
    • 第四个字符是否为e
    • count ++

python实现:


def is_yuan(char):
    return char in "aeiou"


def calc_count(word):
    global count
    # 依次 以每个字符开头 寻找
    for i in range(len(word)-3):

        if not is_yuan(word[i]) and is_yuan(word[i+1]) and \
            (not is_yuan(word[i+2]) and word[i+2] != "r") and word[i+3] == "e":
            count += 1
            print(word)

if __name__ == '__main__':
    sent = input().strip().split()
    n = len(sent)

    # 反转单词字母
    for i in range(n):
        if sent[i].isalpha(): # !!
            sent[i] = sent[i][::-1]

    # 反转后  验证是否有相对双音节
    count = 0
    for word in sent:
        if len(word) < 4:
            continue
        calc_count(word)

    print(count)

java实现:

import java.util.Scanner;

public class Main0045 {
  public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
      String line = scanner.nextLine();
      solution(line);
    }

  }

  private static void solution(String line) {
    String[] words = line.split(" ");

    int count = 0;

    for (String word : words) {
      char[] chars = word.toCharArray();
      if (word.replaceAll("[a-z]+", "").isEmpty()) {
        for (int i = 0, j = chars.length - 1; i < j; i++, j--) {
          char tmp = chars[i];
          chars[i] = chars[j];
          chars[j] = tmp;
        }
      }
      if (chars.length < 4) continue;
      for (int i = 0; i < chars.length - 3; i++) {
        if (isWord(chars[i])
            && isWord(chars[i + 1])
            && isWord(chars[i + 2])
            && isWord(chars[i + 3])
        )
          if (!isVowel(chars[i])
              && isVowel(chars[i + 1])
              && !isVowel(chars[i + 2]) && chars[i + 2] != 'r'
              && chars[i + 3] == 'e') {
            count++;
          }
      }
    }

    System.out.print(count);
  }

  private static boolean isVowel(char c) {
    return c == 'a' || c == 'e' || c == 'i'
        || c == 'o' || c == 'u';
  }

  private static boolean isWord(char c) {
    return c <= 'z' && c >= 'a';
  }
}

 

需要打开多少监控器

某长方形停车场,每个车位上方都有对应监控器,当且仅当在当前车位或者前后左右四个方向任意一个车位范围停车时,监控器才需要打开,给出某一时刻停车场的停车分布,请统计最少需要打开多少个监控器

输入描述
第一行输入m,n表示长宽,满足1 每行有n个0或1的整数,整数间使用一个空格隔开,
表示该行已停车情况,其中0表示空位,1表示已停

输出描述
最少需要打开监控器的数量;

示例一
输入
3 3
0 0 0
0 1 0
0 0 0
输出
1
思路解析和复杂度分析
思路解析
这道题目的主要思路是进行一次扫描遍历,检查每个停车位的状态。如果停车位上停有车(值为1),则将其自身以及上下左右四个方向的车位都标记为需要监控(将值设为1)。注意,由于题目限定的输入格式,我们需要在原始的停车场地图外面添加一圈边界,以方便处理边界车位的情况。为了方便计算上下左右四个方向,我们预先定义一个方向数组,其中包含了当前位置与四个方向偏移的坐标变化量。

在扫描遍历并标记需要监控的车位之后,我们再进行一次遍历,统计需要监控的车位的数量(即值为1的车位的数量),并将结果输出。

复杂度分析
时间复杂度:由于我们进行了两次对停车场的遍历,每次遍历的时间复杂度都是O(mn),其中m和n分别是停车场的长和宽。因此,整个程序的时间复杂度为O(mn)。

空间复杂度:我们使用了一个二维数组来存储停车场的状态,其大小为(m+2)*(n+2),所以空间复杂度为O(mn)。

由于m和n的上限为20,所以这个算法在时间和空间上都是十分高效的。

 

你可能感兴趣的:(算法与数据结构(python),算法)