Skip to content

Commit d3d51b9

Browse files
authored
Improved task 1977.
1 parent 8549803 commit d3d51b9

File tree

1 file changed

+57
-210
lines changed
  • src/main/java/g1901_2000/s1977_number_of_ways_to_separate_numbers

1 file changed

+57
-210
lines changed
Lines changed: 57 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,234 +1,81 @@
11
package g1901_2000.s1977_number_of_ways_to_separate_numbers;
22

33
// #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%)
55

6-
import java.util.Arrays;
7-
8-
@SuppressWarnings("java:S3012")
96
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;
1112

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;
5419
}
5520
}
56-
tmp = prev;
57-
prev = next;
58-
next = tmp;
5921
}
60-
return (int) prev[n];
6122
}
6223

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);
6528
}
6629

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;
8136
}
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;
9645
} 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];
10056
}
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;
20661
}
20762
}
208-
i1++;
63+
dps[i][j] = sum = (sum + dp[i][j]) % mod;
20964
}
21065
}
66+
}
21167

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;
23271
}
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];
23380
}
23481
}

0 commit comments

Comments
 (0)