算法数据结构(三十八)----DC3算法

2024-06-13 12:18

本文主要是介绍算法数据结构(三十八)----DC3算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

后缀和数组

        后缀数组其实代表所有的后缀字符串在排完名之后,从第0名到第7名依次写下来,这就是所谓的后缀数组

        不会有相同的排名,因为长度不一样


生成后缀数组

1)暴力求解:

        先生成所有的后缀字符串,复杂度O(N^2)

        后缀字符串数量O(N),N个后缀字符串排序,代价O(N*logN)

2)DC3求解:O(N)

        根据下标模3来分组,做了一个类似于递归的事情把这个问题快速解决,有一个前提,初始时,数组里头每个值不要太大,太大的话,常数项就会有点大


DC3步骤

1)首先,做小标分类

i%3==0 ==>0类小标,代号s0

i%3==1 ==>1类小标,代号s1

i%3==2 ==>2类小标,代号s2

2)得到s12类后缀串排名(假设可以得到)

3)当有了s12类的排名后,s0内部的排名过一个基数排序可以直接决定

 4)有了s12类排序和s0类排序后,使用一个merge获得整体的排序s012,

        先比较6开头和5开头的字符串

        s0类跟s2类最多比3回

        s0和s1类最多比较2回

 结论:如果我们有s12的排名,我们就可以用O(N)的时间,拿到s0的内部排名,我们还用O(N)的时间merge  s012 整体出来的排名,后面的时间都是通过基数排序,最多三类数据,所以s12类排名这个事一旦搞定,这个算法就是O(N)

5)怎么得到s12的排名?

1.只拿前三个字符能得出精确排名吗?

2.如果分不出精确排名  

3.组成新数组,s1类放左边,s2类放右边,我缺的就让它挨着

 6)DC3算法生成的后缀数组:sa数组   rank数组

 

public class DC3 {public int[] sa;public int[] rank;public int[] height;// 构造方法的约定:// 数组叫nums,如果你是字符串,请转成整型数组nums// 数组中,最小值>=1// 如果不满足,处理成满足的,也不会影响使用// max, nums里面最大值是多少public DC3(int[] nums, int max) {sa = sa(nums, max);rank = rank();height = height(nums);}private int[] sa(int[] nums, int max) {int n = nums.length;int[] arr = new int[n + 3];for (int i = 0; i < n; i++) {arr[i] = nums[i];}return skew(arr, n, max);}private int[] skew(int[] nums, int n, int K) {int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {if (0 != i % 3) {s12[j++] = i;}}radixPass(nums, s12, sa12, 2, n02, K);radixPass(nums, sa12, s12, 1, n02, K);radixPass(nums, s12, sa12, 0, n02, K);int name = 0, c0 = -1, c1 = -1, c2 = -1;for (int i = 0; i < n02; ++i) {if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {name++;c0 = nums[sa12[i]];c1 = nums[sa12[i] + 1];c2 = nums[sa12[i] + 2];}if (1 == sa12[i] % 3) {s12[sa12[i] / 3] = name;} else {s12[sa12[i] / 3 + n0] = name;}}if (name < n02) {sa12 = skew(s12, n02, name);for (int i = 0; i < n02; i++) {s12[sa12[i]] = i + 1;}} else {for (int i = 0; i < n02; i++) {sa12[s12[i] - 1] = i;}}int[] s0 = new int[n0], sa0 = new int[n0];for (int i = 0, j = 0; i < n02; i++) {if (sa12[i] < n0) {s0[j++] = 3 * sa12[i];}}radixPass(nums, s0, sa0, 0, n0, K);int[] sa = new int[n];for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;int j = sa0[p];if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]): leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {sa[k] = i;t++;if (t == n02) {for (k++; p < n0; p++, k++) {sa[k] = sa0[p];}}} else {sa[k] = j;p++;if (p == n0) {for (k++; t < n02; t++, k++) {sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;}}}}return sa;}private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {int[] cnt = new int[k + 1];for (int i = 0; i < n; ++i) {cnt[nums[input[i] + offset]]++;}for (int i = 0, sum = 0; i < cnt.length; ++i) {int t = cnt[i];cnt[i] = sum;sum += t;}for (int i = 0; i < n; ++i) {output[cnt[nums[input[i] + offset]]++] = input[i];}}private boolean leq(int a1, int a2, int b1, int b2) {return a1 < b1 || (a1 == b1 && a2 <= b2);}private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));}private int[] rank() {int n = sa.length;int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[sa[i]] = i;}return ans;}private int[] height(int[] s) {int n = s.length;int[] ans = new int[n];for (int i = 0, k = 0; i < n; ++i) {if (rank[i] != 0) {if (k > 0) {--k;}int j = sa[rank[i] - 1];while (i + k < n && j + k < n && s[i + k] == s[j + k]) {++k;}ans[rank[i]] = k;}}return ans;}// 为了测试public static int[] randomArray(int len, int maxValue) {int[] arr = new int[len];for (int i = 0; i < len; i++) {arr[i] = (int) (Math.random() * maxValue) + 1;}return arr;}// 为了测试public static void main(String[] args) {int len = 100000;int maxValue = 100;long start = System.currentTimeMillis();new DC3(randomArray(len, maxValue), maxValue);long end = System.currentTimeMillis();System.out.println("数据量 " + len + ", 运行时间 " + (end - start) + " ms");}}

 题目一

https://leetcode.com/problems/last-substring-in-lexicographical-order/

给你一个字符串 s,找出它的所有子串并按字典序排列,返回排在最后的那个子串。

利用DC3的sa数组求解

public static String lastSubstring(String s) {if (s == null || s.length() == 0) {return s;}int N = s.length();char[] str = s.toCharArray();int min = Integer.MAX_VALUE;int max = Integer.MIN_VALUE;for (char cha : str) {min = Math.min(min, cha);max = Math.max(max, cha);}int[] arr = new int[N];for (int i = 0; i < N; i++) {arr[i] = str[i] - min + 1;}DC3 dc3 = new DC3(arr, max - min + 1);return s.substring(dc3.sa[N - 1]);}public static class DC3 {public int[] sa;public DC3(int[] nums, int max) {sa = sa(nums, max);}private int[] sa(int[] nums, int max) {int n = nums.length;int[] arr = new int[n + 3];for (int i = 0; i < n; i++) {arr[i] = nums[i];}return skew(arr, n, max);}private int[] skew(int[] nums, int n, int K) {int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {if (0 != i % 3) {s12[j++] = i;}}radixPass(nums, s12, sa12, 2, n02, K);radixPass(nums, sa12, s12, 1, n02, K);radixPass(nums, s12, sa12, 0, n02, K);int name = 0, c0 = -1, c1 = -1, c2 = -1;for (int i = 0; i < n02; ++i) {if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {name++;c0 = nums[sa12[i]];c1 = nums[sa12[i] + 1];c2 = nums[sa12[i] + 2];}if (1 == sa12[i] % 3) {s12[sa12[i] / 3] = name;} else {s12[sa12[i] / 3 + n0] = name;}}if (name < n02) {sa12 = skew(s12, n02, name);for (int i = 0; i < n02; i++) {s12[sa12[i]] = i + 1;}} else {for (int i = 0; i < n02; i++) {sa12[s12[i] - 1] = i;}}int[] s0 = new int[n0], sa0 = new int[n0];for (int i = 0, j = 0; i < n02; i++) {if (sa12[i] < n0) {s0[j++] = 3 * sa12[i];}}radixPass(nums, s0, sa0, 0, n0, K);int[] sa = new int[n];for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;int j = sa0[p];if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]): leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {sa[k] = i;t++;if (t == n02) {for (k++; p < n0; p++, k++) {sa[k] = sa0[p];}}} else {sa[k] = j;p++;if (p == n0) {for (k++; t < n02; t++, k++) {sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;}}}}return sa;}private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {int[] cnt = new int[k + 1];for (int i = 0; i < n; ++i) {cnt[nums[input[i] + offset]]++;}for (int i = 0, sum = 0; i < cnt.length; ++i) {int t = cnt[i];cnt[i] = sum;sum += t;}for (int i = 0; i < n; ++i) {output[cnt[nums[input[i] + offset]]++] = input[i];}}private boolean leq(int a1, int a2, int b1, int b2) {return a1 < b1 || (a1 == b1 && a2 <= b2);}private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));}}

题目二

给定两个字符串str1str2

想把str2整体插入到str1中的某个位置

形成最大的字典序

返回字典序最大的结果

// 暴力方法public static String right(String s1, String s2) {if (s1 == null || s1.length() == 0) {return s2;}if (s2 == null || s2.length() == 0) {return s1;}String p1 = s1 + s2;String p2 = s2 + s1;String ans = p1.compareTo(p2) > 0 ? p1 : p2;for (int end = 1; end < s1.length(); end++) {String cur = s1.substring(0, end) + s2 + s1.substring(end);if (cur.compareTo(ans) > 0) {ans = cur;}}return ans;}
// 正式方法 O(N+M) + O(M^2) DC3的rank数组// N : s1长度// M : s2长度public static String maxCombine(String s1, String s2) {if (s1 == null || s1.length() == 0) {return s2;}if (s2 == null || s2.length() == 0) {return s1;}char[] str1 = s1.toCharArray();char[] str2 = s2.toCharArray();int N = str1.length;int M = str2.length;int min = str1[0];int max = str1[0];for (int i = 1; i < N; i++) {min = Math.min(min, str1[i]);max = Math.max(max, str1[i]);}for (int i = 0; i < M; i++) {min = Math.min(min, str2[i]);max = Math.max(max, str2[i]);}int[] all = new int[N + M + 1];int index = 0;for (int i = 0; i < N; i++) {all[index++] = str1[i] - min + 2;}all[index++] = 1;for (int i = 0; i < M; i++) {all[index++] = str2[i] - min + 2;}DC3 dc3 = new DC3(all, max - min + 2);int[] rank = dc3.rank;int comp = N + 1;for (int i = 0; i < N; i++) {if (rank[i] < rank[comp]) {int best = bestSplit(s1, s2, i);return s1.substring(0, best) + s2 + s1.substring(best);}}return s1 + s2;}public static int bestSplit(String s1, String s2, int first) {int N = s1.length();int M = s2.length();int end = N;for (int i = first, j = 0; i < N && j < M; i++, j++) {if (s1.charAt(i) < s2.charAt(j)) {end = i;break;}}String bestPrefix = s2;int bestSplit = first;for (int i = first + 1, j = M - 1; i <= end; i++, j--) {String curPrefix = s1.substring(first, i) + s2.substring(0, j);if (curPrefix.compareTo(bestPrefix) >= 0) {bestPrefix = curPrefix;bestSplit = i;}}return bestSplit;}public static class DC3 {public int[] sa;public int[] rank;public DC3(int[] nums, int max) {sa = sa(nums, max);rank = rank();}private int[] sa(int[] nums, int max) {int n = nums.length;int[] arr = new int[n + 3];for (int i = 0; i < n; i++) {arr[i] = nums[i];}return skew(arr, n, max);}private int[] skew(int[] nums, int n, int K) {int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {if (0 != i % 3) {s12[j++] = i;}}radixPass(nums, s12, sa12, 2, n02, K);radixPass(nums, sa12, s12, 1, n02, K);radixPass(nums, s12, sa12, 0, n02, K);int name = 0, c0 = -1, c1 = -1, c2 = -1;for (int i = 0; i < n02; ++i) {if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {name++;c0 = nums[sa12[i]];c1 = nums[sa12[i] + 1];c2 = nums[sa12[i] + 2];}if (1 == sa12[i] % 3) {s12[sa12[i] / 3] = name;} else {s12[sa12[i] / 3 + n0] = name;}}if (name < n02) {sa12 = skew(s12, n02, name);for (int i = 0; i < n02; i++) {s12[sa12[i]] = i + 1;}} else {for (int i = 0; i < n02; i++) {sa12[s12[i] - 1] = i;}}int[] s0 = new int[n0], sa0 = new int[n0];for (int i = 0, j = 0; i < n02; i++) {if (sa12[i] < n0) {s0[j++] = 3 * sa12[i];}}radixPass(nums, s0, sa0, 0, n0, K);int[] sa = new int[n];for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;int j = sa0[p];if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]): leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {sa[k] = i;t++;if (t == n02) {for (k++; p < n0; p++, k++) {sa[k] = sa0[p];}}} else {sa[k] = j;p++;if (p == n0) {for (k++; t < n02; t++, k++) {sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;}}}}return sa;}private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {int[] cnt = new int[k + 1];for (int i = 0; i < n; ++i) {cnt[nums[input[i] + offset]]++;}for (int i = 0, sum = 0; i < cnt.length; ++i) {int t = cnt[i];cnt[i] = sum;sum += t;}for (int i = 0; i < n; ++i) {output[cnt[nums[input[i] + offset]]++] = input[i];}}private boolean leq(int a1, int a2, int b1, int b2) {return a1 < b1 || (a1 == b1 && a2 <= b2);}private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));}private int[] rank() {int n = sa.length;int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[sa[i]] = i;}return ans;}}

题目三

https://leetcode.com/problems/create-maximum-number/

 给两个长度分别为MN的整型数组nums1nums2其中每个值都不大于9,再给定一个正数K你可以在nums1nums2中挑选数字,要求一共挑选K个,并且要从左到右挑。返回所有可能的结果中,代表最大数字的结果。

public static int[] maxNumber1(int[] nums1, int[] nums2, int k) {int len1 = nums1.length;int len2 = nums2.length;if (k < 0 || k > len1 + len2) {return null;}int[] res = new int[k];int[][] dp1 = getdp(nums1); // 生成dp1这个表,以后从nums1中,只要固定拿N个数,int[][] dp2 = getdp(nums2);// get1 从arr1里拿的数量// K - get1 从arr2里拿的数量for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {// arr1 挑 get1个,怎么得到一个最优结果int[] pick1 = maxPick(nums1, dp1, get1);int[] pick2 = maxPick(nums2, dp2, k - get1);int[] merge = merge(pick1, pick2);res = preMoreThanLast(res, 0, merge, 0) ? res : merge;}return res;}public static int[] merge(int[] nums1, int[] nums2) {int k = nums1.length + nums2.length;int[] ans = new int[k];for (int i = 0, j = 0, r = 0; r < k; ++r) {ans[r] = preMoreThanLast(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];}return ans;}public static boolean preMoreThanLast(int[] nums1, int i, int[] nums2, int j) {while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {i++;j++;}return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);}public static int[][] getdp(int[] arr) {int size = arr.length; // 0~N-1int pick = arr.length + 1; // 1 ~ Nint[][] dp = new int[size][pick];// get 不从0开始,因为拿0个无意义for (int get = 1; get < pick; get++) { // 1 ~ Nint maxIndex = size - get;// i~N-1for (int i = size - get; i >= 0; i--) {if (arr[i] >= arr[maxIndex]) {maxIndex = i;}dp[i][get] = maxIndex;}}return dp;}public static int[] maxPick(int[] arr, int[][] dp, int pick) {int[] res = new int[pick];for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) {res[resIndex] = arr[dp[dpRow][pick]];dpRow = dp[dpRow][pick] + 1;}return res;}
public static int[] maxNumber2(int[] nums1, int[] nums2, int k) {int len1 = nums1.length;int len2 = nums2.length;if (k < 0 || k > len1 + len2) {return null;}int[] res = new int[k];int[][] dp1 = getdp(nums1);int[][] dp2 = getdp(nums2);for (int get1 = Math.max(0, k - len2); get1 <= Math.min(k, len1); get1++) {int[] pick1 = maxPick(nums1, dp1, get1);int[] pick2 = maxPick(nums2, dp2, k - get1);int[] merge = mergeBySuffixArray(pick1, pick2);res = moreThan(res, merge) ? res : merge;}return res;}public static boolean moreThan(int[] pre, int[] last) {int i = 0;int j = 0;while (i < pre.length && j < last.length && pre[i] == last[j]) {i++;j++;}return j == last.length || (i < pre.length && pre[i] > last[j]);}public static int[] mergeBySuffixArray(int[] nums1, int[] nums2) {int size1 = nums1.length;int size2 = nums2.length;int[] nums = new int[size1 + 1 + size2];for (int i = 0; i < size1; i++) {nums[i] = nums1[i] + 2;}nums[size1] = 1;for (int j = 0; j < size2; j++) {nums[j + size1 + 1] = nums2[j] + 2;}DC3 dc3 = new DC3(nums, 11);int[] rank = dc3.rank;int[] ans = new int[size1 + size2];int i = 0;int j = 0;int r = 0;while (i < size1 && j < size2) {ans[r++] = rank[i] > rank[j + size1 + 1] ? nums1[i++] : nums2[j++];}while (i < size1) {ans[r++] = nums1[i++];}while (j < size2) {ans[r++] = nums2[j++];}return ans;}public static class DC3 {public int[] sa;public int[] rank;public DC3(int[] nums, int max) {sa = sa(nums, max);rank = rank();}private int[] sa(int[] nums, int max) {int n = nums.length;int[] arr = new int[n + 3];for (int i = 0; i < n; i++) {arr[i] = nums[i];}return skew(arr, n, max);}private int[] skew(int[] nums, int n, int K) {int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {if (0 != i % 3) {s12[j++] = i;}}radixPass(nums, s12, sa12, 2, n02, K);radixPass(nums, sa12, s12, 1, n02, K);radixPass(nums, s12, sa12, 0, n02, K);int name = 0, c0 = -1, c1 = -1, c2 = -1;for (int i = 0; i < n02; ++i) {if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {name++;c0 = nums[sa12[i]];c1 = nums[sa12[i] + 1];c2 = nums[sa12[i] + 2];}if (1 == sa12[i] % 3) {s12[sa12[i] / 3] = name;} else {s12[sa12[i] / 3 + n0] = name;}}if (name < n02) {sa12 = skew(s12, n02, name);for (int i = 0; i < n02; i++) {s12[sa12[i]] = i + 1;}} else {for (int i = 0; i < n02; i++) {sa12[s12[i] - 1] = i;}}int[] s0 = new int[n0], sa0 = new int[n0];for (int i = 0, j = 0; i < n02; i++) {if (sa12[i] < n0) {s0[j++] = 3 * sa12[i];}}radixPass(nums, s0, sa0, 0, n0, K);int[] sa = new int[n];for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;int j = sa0[p];if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]): leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {sa[k] = i;t++;if (t == n02) {for (k++; p < n0; p++, k++) {sa[k] = sa0[p];}}} else {sa[k] = j;p++;if (p == n0) {for (k++; t < n02; t++, k++) {sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;}}}}return sa;}private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {int[] cnt = new int[k + 1];for (int i = 0; i < n; ++i) {cnt[nums[input[i] + offset]]++;}for (int i = 0, sum = 0; i < cnt.length; ++i) {int t = cnt[i];cnt[i] = sum;sum += t;}for (int i = 0; i < n; ++i) {output[cnt[nums[input[i] + offset]]++] = input[i];}}private boolean leq(int a1, int a2, int b1, int b2) {return a1 < b1 || (a1 == b1 && a2 <= b2);}private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));}private int[] rank() {int n = sa.length;int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[sa[i]] = i;}return ans;}}public static int[][] getdp(int[] arr) {int size = arr.length; // 0~N-1int pick = arr.length + 1; // 1 ~ Nint[][] dp = new int[size][pick];// get 不从0开始,因为拿0个无意义for (int get = 1; get < pick; get++) { // 1 ~ Nint maxIndex = size - get;// i~N-1for (int i = size - get; i >= 0; i--) {if (arr[i] >= arr[maxIndex]) {maxIndex = i;}dp[i][get] = maxIndex;}}return dp;}public static int[] maxPick(int[] arr, int[][] dp, int pick) {int[] res = new int[pick];for (int resIndex = 0, dpRow = 0; pick > 0; pick--, resIndex++) {res[resIndex] = arr[dp[dpRow][pick]];dpRow = dp[dpRow][pick] + 1;}return res;}

题目四

 最长公共子串问题是面试常见题目之一,假设str1长度Nstr2长度M

一般在面试场上回答出O(N*M)的解法已经是比较优秀了

因为得到O(N*M)的解法,就已经需要用到动态规划了

但其实这个问题的最优解是O(N+M),需要用到后缀数组+height数组

height数组:height[i]指在sa中第i名的和第i-1名的最长公共前缀

public static int lcs1(String s1, String s2) {if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {return 0;}char[] str1 = s1.toCharArray();char[] str2 = s2.toCharArray();int row = 0;int col = str2.length - 1;int max = 0;while (row < str1.length) {int i = row;int j = col;int len = 0;while (i < str1.length && j < str2.length) {if (str1[i] != str2[j]) {len = 0;} else {len++;}if (len > max) {max = len;}i++;j++;}if (col > 0) {col--;} else {row++;}}return max;}
// 最长公共子串问题是面试常见题目之一
// 假设str1长度N,str2长度M
// 因为最优解的难度所限,一般在面试场上回答出O(N*M)的解法已经是比较优秀了
// 因为得到O(N*M)的解法,就已经需要用到动态规划了
// 但其实这个问题的最优解是O(N+M),为了达到这个复杂度可是不容易
// 首先需要用到DC3算法得到后缀数组(sa)
// 进而用sa数组去生成height数组
// 而且在生成的时候,还有一个不回退的优化,都非常不容易理解
// 这就是后缀数组在面试算法中的地位 : 德高望重的噩梦
public static int lcs2(String s1, String s2) {if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {return 0;}char[] str1 = s1.toCharArray();char[] str2 = s2.toCharArray();int N = str1.length;int M = str2.length;int min = str1[0];int max = str1[0];for (int i = 1; i < N; i++) {min = Math.min(min, str1[i]);max = Math.max(max, str1[i]);}for (int i = 0; i < M; i++) {min = Math.min(min, str2[i]);max = Math.max(max, str2[i]);}int[] all = new int[N + M + 1];int index = 0;for (int i = 0; i < N; i++) {all[index++] = str1[i] - min + 2;}all[index++] = 1;for (int i = 0; i < M; i++) {all[index++] = str2[i] - min + 2;}DC3 dc3 = new DC3(all, max - min + 2);int n = all.length;int[] sa = dc3.sa;int[] height = dc3.height;int ans = 0;for (int i = 1; i < n; i++) {int Y = sa[i - 1];int X = sa[i];if (Math.min(X, Y) < N && Math.max(X, Y) > N) {ans = Math.max(ans, height[i]);}}return ans;}public static class DC3 {public int[] sa;public int[] rank;public int[] height;public DC3(int[] nums, int max) {sa = sa(nums, max);rank = rank();height = height(nums);}private int[] sa(int[] nums, int max) {int n = nums.length;int[] arr = new int[n + 3];for (int i = 0; i < n; i++) {arr[i] = nums[i];}return skew(arr, n, max);}private int[] skew(int[] nums, int n, int K) {int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3];for (int i = 0, j = 0; i < n + (n0 - n1); ++i) {if (0 != i % 3) {s12[j++] = i;}}radixPass(nums, s12, sa12, 2, n02, K);radixPass(nums, sa12, s12, 1, n02, K);radixPass(nums, s12, sa12, 0, n02, K);int name = 0, c0 = -1, c1 = -1, c2 = -1;for (int i = 0; i < n02; ++i) {if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) {name++;c0 = nums[sa12[i]];c1 = nums[sa12[i] + 1];c2 = nums[sa12[i] + 2];}if (1 == sa12[i] % 3) {s12[sa12[i] / 3] = name;} else {s12[sa12[i] / 3 + n0] = name;}}if (name < n02) {sa12 = skew(s12, n02, name);for (int i = 0; i < n02; i++) {s12[sa12[i]] = i + 1;}} else {for (int i = 0; i < n02; i++) {sa12[s12[i] - 1] = i;}}int[] s0 = new int[n0], sa0 = new int[n0];for (int i = 0, j = 0; i < n02; i++) {if (sa12[i] < n0) {s0[j++] = 3 * sa12[i];}}radixPass(nums, s0, sa0, 0, n0, K);int[] sa = new int[n];for (int p = 0, t = n0 - n1, k = 0; k < n; k++) {int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;int j = sa0[p];if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]): leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) {sa[k] = i;t++;if (t == n02) {for (k++; p < n0; p++, k++) {sa[k] = sa0[p];}}} else {sa[k] = j;p++;if (p == n0) {for (k++; t < n02; t++, k++) {sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2;}}}}return sa;}private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) {int[] cnt = new int[k + 1];for (int i = 0; i < n; ++i) {cnt[nums[input[i] + offset]]++;}for (int i = 0, sum = 0; i < cnt.length; ++i) {int t = cnt[i];cnt[i] = sum;sum += t;}for (int i = 0; i < n; ++i) {output[cnt[nums[input[i] + offset]]++] = input[i];}}private boolean leq(int a1, int a2, int b1, int b2) {return a1 < b1 || (a1 == b1 && a2 <= b2);}private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) {return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3));}private int[] rank() {int n = sa.length;int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[sa[i]] = i;}return ans;}private int[] height(int[] s) {int n = s.length;int[] ans = new int[n];// 依次求h[i] , k = 0for (int i = 0, k = 0; i < n; ++i) {if (rank[i] != 0) {if (k > 0) {--k;}int j = sa[rank[i] - 1];while (i + k < n && j + k < n && s[i + k] == s[j + k]) {++k;}// h[i] = kans[rank[i]] = k;}}return ans;}}

这篇关于算法数据结构(三十八)----DC3算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057277

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯: