|
1 | 1 | package g1901_2000.s1977_number_of_ways_to_separate_numbers;
|
2 | 2 |
|
3 | 3 | // #Hard #String #Dynamic_Programming #Suffix_Array
|
4 |
| -// #2022_05_21_Time_150_ms_(94.52%)_Space_101.1_MB_(90.41%) |
| 4 | +// #2022_05_26_Time_604_ms_(59.21%)_Space_359.3_MB_(5.26%) |
5 | 5 |
|
6 |
| -import java.util.Arrays; |
7 |
| - |
8 |
| -@SuppressWarnings("java:S3012") |
9 | 6 | public class Solution {
|
10 |
| - private static final int P = 1_000_000_007; |
| 7 | + private int[][] lcp; |
| 8 | + private long[][] dp; |
| 9 | + private long[][] dps; |
| 10 | + private String num; |
| 11 | + private int n; |
11 | 12 |
|
12 |
| - public int numberOfCombinations(String num) { |
13 |
| - int n = num.length(); |
14 |
| - if (num.charAt(0) == '0') { |
15 |
| - return 0; |
16 |
| - } |
17 |
| - if (n == 1) { |
18 |
| - return 1; |
19 |
| - } |
20 |
| - int[] arr = new int[n]; |
21 |
| - for (int i = 0; i < n; i++) { |
22 |
| - arr[i] = num.charAt(i) - '0' + 1; |
23 |
| - } |
24 |
| - int[] suffixArray = new SuffixArrayTools().buildSuffixArray(arr); |
25 |
| - int[] invSuffixArray = new SuffixArrayTools().inverseSuffixArray(suffixArray); |
26 |
| - int[] lcp = |
27 |
| - SuffixArrayTools.buildLongestCommonPrefixArray(arr, suffixArray, invSuffixArray); |
28 |
| - int[][] lcpMat = new int[n - 1][n - 1]; |
29 |
| - for (int i = 0; i < n - 1; i++) { |
30 |
| - lcpMat[i][i] = lcp[i]; |
31 |
| - } |
32 |
| - for (int i = 0; i < n - 2; i++) { |
33 |
| - for (int j = i + 1; j < n - 1; j++) { |
34 |
| - lcpMat[i][j] = Math.min(lcpMat[i][j - 1], lcpMat[j][j]); |
35 |
| - } |
36 |
| - } |
37 |
| - long[] prev = new long[n + 1]; |
38 |
| - long[] next = new long[n + 1]; |
39 |
| - long[] tmp; |
40 |
| - prev[0] = 1; |
41 |
| - next[0] = 1; |
42 |
| - for (int d = 1; d <= n; d++) { |
43 |
| - System.arraycopy(prev, 1, next, 1, n); |
44 |
| - for (int j1 = 0, i1 = j1 - d, j2 = j1 + d; j2 <= n; j1++, j2++, i1++) { |
45 |
| - if (arr[j1] != 1) { |
46 |
| - if (i1 < 0 || arr[i1] != 1 && ok(i1, j1, d, invSuffixArray, lcpMat)) { |
47 |
| - next[j2] += next[j1]; |
48 |
| - } else { |
49 |
| - next[j2] += prev[j1]; |
50 |
| - } |
51 |
| - if (next[j2] >= P) { |
52 |
| - next[j2] -= P; |
53 |
| - } |
| 13 | + // Pre-compute The Longest Common Prefix sequence for each index in the string |
| 14 | + private void calcLCP() { |
| 15 | + for (int i = n - 1; i >= 0; i--) { |
| 16 | + for (int j = n - 1; j >= 0; j--) { |
| 17 | + if (num.charAt(i) == num.charAt(j)) { |
| 18 | + lcp[i][j] = lcp[i + 1][j + 1] + 1; |
54 | 19 | }
|
55 | 20 | }
|
56 |
| - tmp = prev; |
57 |
| - prev = next; |
58 |
| - next = tmp; |
59 | 21 | }
|
60 |
| - return (int) prev[n]; |
61 | 22 | }
|
62 | 23 |
|
63 |
| - private boolean ok(int start1, int start2, int len, int[] inv, int[][] lcpMat) { |
64 |
| - return inv[start1] < inv[start2] || lcpMat[inv[start2]][inv[start1] - 1] >= len; |
| 24 | + // compare substring of same length for value |
| 25 | + private boolean compare(int i, int j, int len) { |
| 26 | + int common = lcp[i][j]; |
| 27 | + return common >= len || num.charAt(i + common) < num.charAt(j + common); |
65 | 28 | }
|
66 | 29 |
|
67 |
| - static class SuffixArrayTools { |
68 |
| - int[] buildSuffixArray(int[] arr) { |
69 |
| - int n = arr.length; |
70 |
| - int[] s = Arrays.copyOf(arr, n + 3); |
71 |
| - int[] sa = new int[n]; |
72 |
| - suffixArray(s, sa, n, 10); |
73 |
| - return sa; |
74 |
| - } |
75 |
| - |
76 |
| - private int[] inverseSuffixArray(int[] suffixArray) { |
77 |
| - int n = suffixArray.length; |
78 |
| - int[] ans = new int[n]; |
79 |
| - for (int i = 0; i < n; i++) { |
80 |
| - ans[suffixArray[i]] = i; |
| 30 | + // calculates number of possible separations |
| 31 | + private void calcResult() { |
| 32 | + for (int i = n - 1; i >= 0; i--) { |
| 33 | + // leading zero at current current index |
| 34 | + if (num.charAt(i) == '0') { |
| 35 | + continue; |
81 | 36 | }
|
82 |
| - return ans; |
83 |
| - } |
84 |
| - |
85 |
| - static int[] buildLongestCommonPrefixArray( |
86 |
| - int[] arr, int[] suffixArray, int[] invSuffixArray) { |
87 |
| - int n = arr.length; |
88 |
| - int[] lcp = new int[n - 1]; |
89 |
| - int k = 0; |
90 |
| - for (int i = 0; i < n; i++) { |
91 |
| - if (k > 0) { |
92 |
| - k--; |
93 |
| - } |
94 |
| - if (invSuffixArray[i] == n - 1) { |
95 |
| - k = 0; |
| 37 | + // for substring starting at index i |
| 38 | + long sum = 0; |
| 39 | + for (int j = n - 1; j >= i; j--) { |
| 40 | + long mod = 1000000007; |
| 41 | + if (j == n - 1) { |
| 42 | + // whole substring from index i is a valid possible list of integer (single |
| 43 | + // integer in this case) |
| 44 | + dp[i][j] = 1; |
96 | 45 | } else {
|
97 |
| - int j = suffixArray[invSuffixArray[i] + 1]; |
98 |
| - while (i + k < n && j + k < n && arr[i + k] == arr[j + k]) { |
99 |
| - k++; |
| 46 | + // first integer (i-j) |
| 47 | + int len = j - i + 1; |
| 48 | + // second integer start index |
| 49 | + int st = j + 1; |
| 50 | + // second integer end index |
| 51 | + int ed = st + len - 1; |
| 52 | + // equal length integers should be compared for value |
| 53 | + dp[i][j] = 0; |
| 54 | + if (ed < n && compare(i, st, len)) { |
| 55 | + dp[i][j] = dp[st][ed]; |
100 | 56 | }
|
101 |
| - lcp[invSuffixArray[i]] = k; |
102 |
| - } |
103 |
| - } |
104 |
| - return lcp; |
105 |
| - } |
106 |
| - |
107 |
| - private boolean leq(int a1, int a2, int b1, int b2) { |
108 |
| - return (a1 < b1 || (a1 == b1 && a2 <= b2)); |
109 |
| - } |
110 |
| - |
111 |
| - private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { |
112 |
| - return (a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3))); |
113 |
| - } |
114 |
| - |
115 |
| - private void suffixArray(int[] s, int[] sa, int n, int k) { |
116 |
| - int n0 = (n + 2) / 3; |
117 |
| - int n1 = (n + 1) / 3; |
118 |
| - int n2 = n / 3; |
119 |
| - int n02 = n0 + n2; |
120 |
| - int[] s12 = new int[n02 + 3]; |
121 |
| - int[] sa12 = new int[n02 + 3]; |
122 |
| - int[] s0 = new int[n0]; |
123 |
| - int[] sa0 = new int[n0]; |
124 |
| - int i = 0; |
125 |
| - int j = 0; |
126 |
| - while (i < n + (n0 - n1)) { |
127 |
| - if (i % 3 != 0) { |
128 |
| - s12[j++] = i; |
129 |
| - } |
130 |
| - i++; |
131 |
| - } |
132 |
| - radixPass(s12, sa12, s, 2, n02, k); |
133 |
| - radixPass(sa12, s12, s, 1, n02, k); |
134 |
| - radixPass(s12, sa12, s, 0, n02, k); |
135 |
| - int name = 0; |
136 |
| - int c0 = -1; |
137 |
| - int c1 = -1; |
138 |
| - int c2 = -1; |
139 |
| - for (i = 0; i < n02; i++) { |
140 |
| - if (s[sa12[i]] != c0 || s[sa12[i] + 1] != c1 || s[sa12[i] + 2] != c2) { |
141 |
| - name++; |
142 |
| - c0 = s[sa12[i]]; |
143 |
| - c1 = s[sa12[i] + 1]; |
144 |
| - c2 = s[sa12[i] + 2]; |
145 |
| - } |
146 |
| - if (sa12[i] % 3 == 1) { |
147 |
| - s12[sa12[i] / 3] = name; |
148 |
| - } else { |
149 |
| - s12[sa12[i] / 3 + n0] = name; |
150 |
| - } |
151 |
| - } |
152 |
| - if (name < n02) { |
153 |
| - suffixArray(s12, sa12, n02, name); |
154 |
| - for (i = 0; i < n02; i++) { |
155 |
| - s12[sa12[i]] = i + 1; |
156 |
| - } |
157 |
| - } else { |
158 |
| - for (i = 0; i < n02; i++) { |
159 |
| - sa12[s12[i] - 1] = i; |
160 |
| - } |
161 |
| - } |
162 |
| - i = 0; |
163 |
| - j = 0; |
164 |
| - while (i < n02) { |
165 |
| - if (sa12[i] < n0) { |
166 |
| - s0[j++] = 3 * sa12[i]; |
167 |
| - } |
168 |
| - i++; |
169 |
| - } |
170 |
| - radixPass(s0, sa0, s, 0, n0, k); |
171 |
| - int p = 0; |
172 |
| - int t = n0 - n1; |
173 |
| - int i1 = 0; |
174 |
| - while (i1 < n) { |
175 |
| - i = getI(sa12, n0, t); |
176 |
| - j = sa0[p]; |
177 |
| - if (sa12[t] < n0 |
178 |
| - ? leq(s[i], s12[sa12[t] + n0], s[j], s12[j / 3]) |
179 |
| - : leq( |
180 |
| - s[i], |
181 |
| - s[i + 1], |
182 |
| - s12[sa12[t] - n0 + 1], |
183 |
| - s[j], |
184 |
| - s[j + 1], |
185 |
| - s12[j / 3 + n0])) { |
186 |
| - sa[i1] = i; |
187 |
| - t++; |
188 |
| - if (t == n02) { |
189 |
| - i1++; |
190 |
| - while (p < n0) { |
191 |
| - sa[i1] = sa0[p]; |
192 |
| - p++; |
193 |
| - i1++; |
194 |
| - } |
195 |
| - } |
196 |
| - } else { |
197 |
| - sa[i1] = j; |
198 |
| - p++; |
199 |
| - if (p == n0) { |
200 |
| - i1++; |
201 |
| - while (t < n02) { |
202 |
| - sa[i1] = getI(sa12, n0, t); |
203 |
| - t++; |
204 |
| - i1++; |
205 |
| - } |
| 57 | + // including the second integers possibilities with length greater than 1st one. |
| 58 | + if (ed + 1 < n) { |
| 59 | + // dps[st][ed+1] => dp[st][ed+1].......dp[st][n-1] |
| 60 | + dp[i][j] = (dp[i][j] + dps[st][ed + 1]) % mod; |
206 | 61 | }
|
207 | 62 | }
|
208 |
| - i1++; |
| 63 | + dps[i][j] = sum = (sum + dp[i][j]) % mod; |
209 | 64 | }
|
210 | 65 | }
|
| 66 | + } |
211 | 67 |
|
212 |
| - private int getI(int[] sa12, int n0, int t) { |
213 |
| - return sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; |
214 |
| - } |
215 |
| - |
216 |
| - private void radixPass(int[] a, int[] b, int[] r, int shift, int n, int k) { |
217 |
| - int[] c = new int[k + 1]; |
218 |
| - for (int i = 0; i < n; i++) { |
219 |
| - c[r[a[i] + shift]]++; |
220 |
| - } |
221 |
| - int i = 0; |
222 |
| - int sum = 0; |
223 |
| - while (i <= k) { |
224 |
| - int t = c[i]; |
225 |
| - c[i] = sum; |
226 |
| - sum += t; |
227 |
| - i++; |
228 |
| - } |
229 |
| - for (i = 0; i < n; i++) { |
230 |
| - b[c[r[a[i] + shift]]++] = a[i]; |
231 |
| - } |
| 68 | + public int numberOfCombinations(String num) { |
| 69 | + if (num.charAt(0) == '0') { |
| 70 | + return 0; |
232 | 71 | }
|
| 72 | + n = num.length(); |
| 73 | + this.num = num; |
| 74 | + lcp = new int[n + 1][n + 1]; |
| 75 | + dp = new long[n + 1][n + 1]; |
| 76 | + dps = new long[n + 1][n + 1]; |
| 77 | + calcLCP(); |
| 78 | + calcResult(); |
| 79 | + return (int) dps[0][0]; |
233 | 80 | }
|
234 | 81 | }
|
0 commit comments