算法数据结构(三十八)----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

相关文章

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

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

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “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]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第