解题思路

分 2 大步:

第 1 步

求出所有边的边权,如:

  1. [0, 4, 2, 2]
  2. [0, 0, 2, 2]
  3. [0, 0, 0, 1]
  4. [0, 0, 0, 0]

本题中的字符串可旋转,考虑在每个字符串后重复一次自身,如 "abcd" 变为 "abcdabcd",这样遍历每个长为 m 的窗口就得到了自身的全部旋转结果。

用动态规划,对两个字符串滑一次,求得它们的最长公共子串长度,即边权。

第 2 步

kruskal 算法——原本求最小生成树的算法——求 最大生成树

所有边入堆,并查集判环,无环时加入边并 union()

答案

AC

  1. import java.util.*;
  2. import java.io.*;
  3. public class Main {
  4. private static char[][] arr;
  5. public static void main(String[] args) throws IOException {
  6. BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  7. PrintWriter out = new PrintWriter(System.out);
  8. StringTokenizer st = new StringTokenizer(in.readLine());
  9. int n = Integer.parseInt(st.nextToken());
  10. int m = Integer.parseInt(st.nextToken());
  11. // 每个字符串重复自己
  12. arr = new char[n][m * 2];
  13. for (int i = 0; i < n; i++) {
  14. char[] s = in.readLine().toCharArray();
  15. for (int j = 0; j < m; j++) {
  16. arr[i][j] = arr[i][j + m] = s[j];
  17. }
  18. }
  19. // 求所有边权
  20. int[][] dist = new int[n][n];
  21. for (int i = 0; i < n - 1; i++) {
  22. for (int j = i + 1; j < n; j++) {
  23. dist[i][j] = lcs(i, j);
  24. }
  25. }
  26. // 所有边入堆
  27. Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt((int[] a) -> -a[2]));
  28. for (int i = 0; i < n - 1; i++) {
  29. for (int j = i + 1; j < n; j++) {
  30. pq.offer(new int[]{i, j, dist[i][j]});
  31. }
  32. }
  33. // Kruskal 算法
  34. int[] root = new int[n];
  35. Arrays.setAll(root, a -> a);
  36. int take = 0;
  37. int ans = 0;
  38. while (take < n - 1) {
  39. int[] p = pq.poll();
  40. if (find(root, p[0]) != find(root, p[1])) {
  41. union(root, p[0], p[1]);
  42. take++;
  43. ans += p[2];
  44. }
  45. }
  46. out.println(ans);
  47. out.flush();
  48. out.close();
  49. }
  50. // 求两旋转字符串的最长公共子串长度
  51. private static int lcs(int x, int y) {
  52. int m = arr[0].length;
  53. int max = 0;
  54. int[][] dp = new int[m + 1][m + 1];
  55. for (int i = 1; i <= m; i++) {
  56. for (int j = 1; j <= m; j++) {
  57. if (arr[x][i - 1] == arr[y][j - 1]) {
  58. dp[i][j] = Math.max(dp[i - 1][j - 1] + 1, dp[i][j]);
  59. max = Math.max(dp[i][j], max);
  60. }
  61. }
  62. }
  63. return Math.min(max, m / 2);
  64. }
  65. // 并查集
  66. private static int find(int[] root, int i) {
  67. return root[i] == i ? i : (root[i] = find(root, root[i]));
  68. }
  69. // 并查集
  70. private static void union(int[] root, int x, int y) {
  71. root[find(root, x)] = find(root, y);
  72. }
  73. }
点赞(2)
 

9.9 分

1 人评分

 

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论