Archive for October 20th, 2013
Hands down,这是我leetcode做的最难的题top 5。别的都是可以用算法说得通的,这题就是纯数学。我这辈子最怕的就是数学,要死要活。
几个要点:
- 找没用过的里面的第(k – 1) / (n – 1)!个数字,放第一位上
- k = (k – 1) % (n – 1)!,第一个数字确定了,剩下这些里面重新找第k个。
- n每次-1,比如第一位确定后有(n-1)!种后面数字的排法,第二位也确定了后面的数字排法就又少了一位(n – 1 – 1)!
还是不是很清楚,今晚睡觉接着想。
public String getPermutation(int n, int k) { boolean[] used = new boolean[n]; // used[i] means the digit i + 1 is used, e.g. used[0] means digit '1' is used StringBuilder sb = new StringBuilder(); int factorial = getFactorial(n - 1); int originalN = n; while (k > 0) { int index = (k - 1) / factorial; int count = 0; for (int i = 0; i < originalN; i++) { if (used[i] == false) count++; if (index == count - 1) { sb.append(i + 1); used[i] = true; break; } } n--; k = (k - 1) % factorial + 1; if (n == 0) break; factorial /= n; } return sb.toString(); } private int getFactorial(int n) { if (n == 1 || n == 0) return 1; return n * getFactorial(n - 1); }
[leetcode] Palindrome Partitioning 2 | 一个String最少切几刀能让分成的substring全是palindrome
Posted October 20, 2013
on:非常非常难,用dfs完全过不了。即使把每个substring是不是palindrome全算好了放在cache里也会挂。必须dp了。
算法:
- 首先把每个substring是不是palindrome先二维dp的做出来,参见上一题的分析。其实这一步可以合在下一步里,但是下一步本身的logic就够难想了,所以这里分开来写。
- 一维dp(为神马是一维啊!!头一次看这样的一维啊!!一维dp却是两层循环啊卧槽!!!),d[i]表示str[i..n]最少切几刀能保证substring全是palindrome。
- 把d[i]先赋成最大值。
- i从后往前,然后j在每个i处从前往后。这样做就能保证在i处,i + 1…j – 1的所有两两combination(中间的小节substring)都已经算出来了,就能用了。如果str[i, j]是个palindrome,那么可以在j 和j + 1中间切一刀,这样的#就是d[j + 1] + 1([i..j], 一刀, [j + 1..本来有几刀])。这样切法可能就比之前算的d[i]小了,于是update。
public int minCut(String s) { int n = s.length(); boolean[][] isPalin = new boolean[n][n]; for (int i = n - 1; i >= 0; i--) { // i is always <= j for (int j = i; j < n; j++) { boolean isPalindrome = s.charAt(i) == s.charAt(j) && (j - i < 2 || isPalin[i + 1][j - 1]); isPalin[i][j] = isPalindrome; } } int[] d = new int[n]; // initialize as cut between every letter for (int i = 0; i < n; i++) { d[i] = n - 1 - i; } for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { if (isPalin[i][j]) { if (j == n - 1) d[i] = 0; else d[i] = Math.min(d[i], d[j + 1] + 1); } } } return d[0]; }
- In: leetcode
- 14 Comments
这个题第二遍做code就写的挺完美的,高兴~~几个要点:
- 直接乘会溢出,所以每次都要两个single digit相乘,最大81,不会溢出。
- 比如385 * 97, 就是个位=5 * 7,十位=8 * 7 + 5 * 9 ,百位=3 * 7 + 8 * 9 …
可以每一位用一个Int表示,存在一个int[]里面。 - 这个数组最大长度是num1.len + num2.len,比如99 * 99,最大不会超过10000,所以4位就够了。
- 这种个位在后面的,不好做(10的0次方,可惜对应位的数组index不是0而是n-1),
所以干脆先把string reverse了代码就清晰好多。 - 最后结果前面的0要清掉。
public String multiply(String num1, String num2) { num1 = new StringBuilder(num1).reverse().toString(); num2 = new StringBuilder(num2).reverse().toString(); // even 99 * 99 is < 10000, so maximaly 4 digits int[] d = new int[num1.length() + num2.length()]; for (int i = 0; i < num1.length(); i++) { int a = num1.charAt(i) - '0'; for (int j = 0; j < num2.length(); j++) { int b = num2.charAt(j) - '0'; d[i + j] += a * b; } } StringBuilder sb = new StringBuilder(); for (int i = 0; i < d.length; i++) { int digit = d[i] % 10; int carry = d[i] / 10; sb.insert(0, digit); if (i < d.length - 1) d[i + 1] += carry; } //trim starting zeros while (sb.length() > 0 && sb.charAt(0) == '0') { sb.deleteCharAt(0); } return sb.length() == 0 ? "0" : sb.toString(); }
Tags: math