《剑指offer第二版》我将JAVA解托管在GitHub链接
/**
* 字符串匹配问题
*/
public class Offer19 {
public boolean solve(String s, String pattern) {
if (s == null || pattern == null) return false;
char[] sArr = s.toCharArray();
char[] pArr = pattern.toCharArray();
return isValid(sArr, pArr) && process(sArr, pArr, 0, 0);
}
// 函数p(i, j)表示s[si, sln]与p[pi, pln]的匹配情况
private boolean process(char[] s, char[] p, int si, int pi) {
if (pi == p.length) return si == s.length;
if (pi+1 == p.length || p[pi+1] != '*') {
return si != s.length && (p[pi] == s[si] || p[pi] == '.') && process(s, p, pi+1, si+1);
}
while (si != s.length && (p[pi] == s[si] || p[pi] == '.')) {
if (process(s, p, si, pi+2)) return true;
si++;
}
return process(s, p, si, pi+2);
}
// private boolean process(char[] s, char[] p, int si, int pi) {
// if(p.length == pi) return si == s.length;
// if(pi+1 == p.length || p[pi+1] != '*') {
// return si != s.length && (s[si] == p[pi] || p[pi] == '.') && process(s, p, si+1, pi+1);
// }
// if(si != s.length && (s[si] == p[pi] || p[pi] == '.')){
// 先令x*匹配一个字符,不成功再令x*一个个去匹配更多,还不成功让x*匹配 ''空
// return process(s, p, si+1, pi+2) || process(s, p, si+1, pi) || process(s, p, si, pi+2);
// }
// return process(s, p, si, pi+2);
// }
private boolean isValid(char[] s, char[] p) {
for (char value : s) {
if (value == '.' || value == '*') return false;
}
for (int i = 0; i < p.length; i++) {
if (p[i] == '*' && (i == 0 || p[i-1] == '*')) return false;
}
return true;
}
// =========================================================================
// 将上述递归改为动态规划,观察上述process函数,发现该函数调用过程中有两个参数是不变的(s, p)
// 也就是说该函数的状态就是si和ei值的组合。把函数p在所有不同参数(si, pi)的情况下的所有返回值看作一个范围
// 该范围是一个(sln+1)*(pln+1)的二维数组,并且p(si, pi)在整个递归过程中依赖的总是p(si+1, pi+1)或p(si+k(k>=0), pi+2)
// 假设dp[i][j]代表p(i, j)的赶回值, 则dp[i][j]只依赖dp[i+1][j+1]或dp[i+k(k>=0)][j+2]。
public boolean solve2(String s, String pattern) {
if (s == null || pattern == null) return false;
char[] sArr = s.toCharArray();
char[] pArr = pattern.toCharArray();
// dp[i][j] 表示s[i, sln]与p[j, pln]的匹配情况
boolean[][] dp = initialDP(sArr, pArr);
for (int i = sArr.length-1; i >= 0; i--) {
for (int j = pArr.length-2; j >= 0; j--) {
if (pArr[j+1] != '*') {
dp[i][j] = (sArr[i] == pArr[j] || pArr[j] == '.') && dp[i+1][j+1];
}else {
int si = i;
while (si != sArr.length && (sArr[si] == pArr[j] || pArr[j] == '.')) {
if (dp[si][j+2]) {
dp[i][j] = true;
break;
}
si++;
}
if (!dp[i][j]) {
dp[i][j] = dp[si][j+2];
}
}
}
}
return dp[0][0];
}
private boolean[][] initialDP(char[] sArr, char[] pArr) {
int sln = sArr.length;
int pln = pArr.length;
boolean[][] dp = new boolean[sln+1][pln+1];
dp[sln][pln] = true;
for (int i = pln-2; i >= 0; i=i-2) {
if (pArr[i] != '*' && pArr[i+1] == '*') dp[sln][i] = true;
else break;
}
if (sln>0 && pln>0) {
if (sArr[sln-1] == pArr[pln-1] || pArr[pln-1] == '.') dp[sln-1][pln-1] = true;
}
return dp;
}
}
题目很简单,为了更好的鲁棒性引入了泛型,使用者可以自己传一个比较器Comparator否则元素必须是Comparable可比较的。
/**
* 实现一个栈,要求pop, push, min三个操作皆为O(1)复杂度
* @param
*/
public class Odder30<Value> {
private Node<Value> head; // 主栈
private Node<Value> minHead; // 辅助栈,若新元素比栈顶小则压入,否则压入与栈顶相同的值。
private Comparator<? super Value> comparator; // 比较器
public Odder30(Comparator<? super Value> comparator) {
this.comparator = comparator;
}
public Odder30() {
}
public void push(Value value) {
Value minValue = value; // 代表新值与栈顶值中小的那个
if (minHead != null) {
if (comparator == null) {
@SuppressWarnings("unchecked")
// 若非Comparable类型则直接抛异常
Comparable<? super Value> val = (Comparable<? super Value>) value;
minValue = val.compareTo(minHead.val) > 0 ? minHead.val : minValue;
}else {
minValue = comparator.compare(value, minHead.val) <= 0 ? minValue : minHead.val;
}
}
Node<Value> old = head;
head = new Node<>(value);
head.next = old;
Node<Value> minOld = minHead;
minHead = new Node<>(minValue);
minHead.next = minOld;
}
public Value pop() {
if (head == null) throw new IllegalStateException("stake is empty");
// 辅助站也同步删除栈顶元素
Node<Value> minNext = minHead;
minHead.next = null;
minHead = minNext;
Node<Value> res = head;
Node<Value> next = head.next;
head.next = null; // help GC
head = next;
return res.val;
}
public Value min() {
if (minHead == null) {
throw new IllegalStateException("stack is empty");
}
return minHead.val;
}
}
二叉树的层级打印与Z型打印
/**
* 打印二叉树
*/
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
public class Offer32 {
public void solve(TreeNode head) {
if (head == null) return;
ArrayDeque<TreeNode> queue = new ArrayDeque<>();
queue.addLast(head);
while (!queue.isEmpty()) {
TreeNode node = queue.pollFirst();
System.out.print(node.val + " ");
if (node.left != null) queue.addLast(node.left);
if (node.right != null) queue.addLast(node.right);
}
}
// 一层一行打印
public void solve2(TreeNode head) {
if (head == null) return;
ArrayDeque<TreeNode> queue = new ArrayDeque<>();
queue.addLast(head);
TreeNode last = head; // 当前遍历层的最右边界节点
TreeNode nLast = null; // 当前层的下一层的最右边界节点
while (!queue.isEmpty()) {
TreeNode node = queue.pollFirst();
System.out.print(node.val + " ");
if (node.left != null) {
queue.addLast(node.left);
nLast = head.left;
}
if (node.right != null) {
queue.addLast(node.right);
nLast = head.right;
}
if (node == last && !queue.isEmpty()) {
System.out.print("\n");
last = nLast;
}
}
}
// Z字型打印
// Level 1 left to right: 1
// Level 2 right to left: 3 2
// Level 3 left to right: 4 5 6 7
public void solve3(TreeNode head) {
if (head == null) return;
Deque<TreeNode> deque = new LinkedList<>(); // 双端队列
TreeNode last = head; // 当前层的最后节点
TreeNode nLast = null; // 下一层的最后节点
int level = 1; // 层
boolean lToR = true; // 从左到右为true,反之为false
deque.add(head);
printLevelAndOrientation(level, lToR);
while (!deque.isEmpty()) {
TreeNode node;
if (lToR) {
node = deque.pollFirst();
// 将当前节点的子孩子按左右顺序加到队列末尾
if (node.left != null) {
nLast = nLast == null ? node.left : nLast; // 每层结束后会将nLast归为null
deque.addLast(node.left);
}
if (node.right != null) {
nLast = nLast == null ? node.right : nLast;
deque.addLast(node.right);
}
}
else {
node = deque.pollLast();
// 将当前节点的子孩子按右左顺序加到队列头
if (node.right != null) {
nLast = nLast == null ? node.right : nLast;
deque.addFirst(node.right);
}
if (node.left != null) {
nLast = nLast == null ? node.left : nLast;
deque.addFirst(node.left);
}
}
System.out.print(node.val + " ");
if (node == last && !deque.isEmpty()) {
last = nLast;
//归为null作为标记,因为是之字打印,
//下一行的最后节点为当前行开始节点的最外子孩子
nLast = null;
System.out.println(); // 换行
lToR = !lToR; //反转方向
printLevelAndOrientation(level++, lToR);
}
}
}
// 打印层级与方向
private void printLevelAndOrientation(int level, boolean lToR) {
System.out.println("Level " + level + " from ");
System.out.println(lToR ? "left to right: " : "right to left: ");
}
}
题7是前,中序数组重构二叉树;
题33是判断数组是否是某颗二叉查找树的后序遍历,数组无重复元素。
进阶题目是:后序数组重构二叉查找树,数组无重复元素。
共同点都是先找到头节点,然后将数组分为左右两部分,递归解决问题。
public class Offer7 {
public TreeNode preInToTree(int[] pre, int[] in) {
if (pre == null || in == null) return null;
//记录中序数组的元素下标,用来计算子树大小
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < in.length; i++) {
map.put(in[i], i);
}
return preIn(pre, 0, pre.length-1, in, 0, in.length-1, map);
}
// 第4,6个参数并没有用到,只是这样便于理解
private TreeNode preIn(int[] pre, int pi, int pj, int[] in, int ni, int nj,
Map<Integer, Integer> map) {
if (pi > pj) return null;
TreeNode head = new TreeNode(pre[pi]);
int index = map.get(pre[pi]);
head.left = preIn(pre, pi+1, pi+index-ni, in, ni, index-1, map);
head.right = preIn(pre, pi+index-ni+1,pj, in, index+1, nj, map);
return head;
}
}
/**
* 判断数组是否是某颗二叉查找树的后续遍历,数组无重复重复元素
*/
public class Offer33 {
public boolean solve(int[] arr) {
if (arr == null || arr.length == 0) return false;
return process(arr, 0, arr.length-1);
}
private boolean process(int[] arr, int start, int end) {
if (start == end || start + 1 == end) return true;
int less = -1; // 代表最大(指下标最大)小于根节点(arr[end])的位置
int more = end; // 代表最小(指下标最小)大于根节点(arr[end])的位置
for (int i = start; i < end; i++) {
if (arr[end] > arr[i]) {
less = i;
}else {
more = more == end ? i : more;
}
}
if (less == -1 || more == end) {
return process(arr, start, end-1);
}
if (less != more-1) {
return false;
}
return process(arr, start, less) && process(arr, more, end-1);
}
// 进阶题目:后序数组重建二叉查找树
public TreeNode rebuildBST(int[] arr) {
if (arr == null || arr.length == 0) return null;
return processRe(arr, 0, arr.length-1);
}
private TreeNode processRe(int[] arr, int start, int end) {
if (start == end) return new TreeNode(arr[start]);
TreeNode head = new TreeNode(arr[end]);
int more = end;
for (int i = start; i < end; i++) {
if (arr[end] < arr[i]) {
more = i;
break;
}
}
head.left = processRe(arr, start, more-1);
head.right = processRe(arr, more, end-1);
return head;
}
}
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
/**
*输入一颗二叉树和一个整数,
*打印出二叉树中结点值的和为输入整数的所有路径。
*路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
*/
public class Question25 {
class BinaryTreeNode{
BinaryTreeNode left;
BinaryTreeNode right;
int val;
public BinaryTreeNode(int val) {
this.val = val;
}
}
public void findPath(BinaryTreeNode root, int expectNum) {
if (root == null) {
return;
}
ArrayDeque<Integer> deque= new ArrayDeque<>();
int curNum = 0;
findPath(root, expectNum, deque, curNum);
}
private void findPath(BinaryTreeNode root, int expectNum, ArrayDeque<Integer> deque, int curNum) {
curNum += root.val;
deque.addLast(root.val);
if (root.left == null && root.right == null && expectNum == curNum) {
for (int path : deque) { // 不会删除stack中元素
System.out.print(path + " ");
}
System.out.println();
}
if (root.left != null) {
findPath(root.left, expectNum, deque, curNum);
}
if (root.right != null) {
findPath(root.right, expectNum, deque, curNum);
}
deque.pollLast();
}
}
利用前序遍历来解决。
该题关于路径的定义实际上降低了该题的难度。来看看下面几个进阶的问题。
1, 未排序正数数组中累加和为给定值的最长子数组的长度
/**
* 未排序正数数组中累加和为给定值的最长子序列的长度
*/
public class Page354 {
public int solve(int[] arr, int k) {
if (arr == null || arr.length == 0) return -1;
// [left, right]代表一个移动窗口, sum为该窗口的和
int left = 0, right = 0, len = 0, sum = arr[0];
while (right < arr.length) {
if (sum == k) {
len = Math.max(len, right-left+1);
sum -= arr[left++];
}else if (sum > k) {
sum -= arr[left++];
}else {
right++;
if (right == arr.length) break;
sum += arr[right];
}
}
return len;
}
}
2, 未排序数组中有正有负有0,求累加和为给定值的最长子数组长度
该题的相关变种:
1, 未排序数组中有正有负有0,求正数与负数个数相等的最长子数组长度?将正数变为1,负数为-1,问题转化为求给定值为0的最长子数组长度
2, 未排序数组中只有1和0,求1和0数目相等的最长子数组长度?将0变为-1,问题转化为求给定值为0的最长子数组长度。
/**
* 未排序数组中有正有负有0,求累加和为给定值的最长子数组长度
* 补充问题一:未排序数组中有正有负有0,求正数与负数个数相等的最长子数组长度?将正数变为1,负数为-1,
* 问题转化为求给定值为0的最长子数组长度
* 补充问题二: 未排序数组中只有1和0,求1和0数目相等的最长子数组长度?将0变为-1,问题转化为求
* 给定值为0的最长子数组长度。
*/
public class Page355 {
public int solve(int[] arr, int k) {
if (arr == null || arr.length == 0) return -1;
// key:从arr[0]开始到当前的累加和;value:该累加和第一次出现的位置i值
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1); // 为了将arr[0]包括进来
int sum = 0, len = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
if (map.containsKey(sum -k)) {
len = Math.max(i-map.get(sum-k), len);
}
if (!map.containsKey(sum)) map.put(sum, i);
}
return len;
}
}
分析:假设 f( i )代表arr[0 ~ i] 的和,f ( j )代表arr[0 ~ j] 的和,若 f( i ) - f( j ) = k,则代表路径存在,由此得出上述代码。
与该题思路类似,将该种方法引入到二叉树的路径查找中来。
题目:在二叉树中找到累加和为指定值的最长路径长度, 路径可从任意节点开始到任意节点结束,不同于《Offer》中的第34题的定义
/**
* 在二叉树中找到累加和为指定值的最长路径长度
*/
public class Page115 {
private int maxLen;
public int solve(TreeNode head, int target) {
if (head == null) return -1;
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 0); //重要
preOrder(head, target, 0, 1, map);
return maxLen;
}
// 就像该函数名所表明的那样,解决思路是利用前序遍历
private void preOrder(TreeNode head, int target, int preSum, int level,
Map<Integer, Integer> map) {
if (head == null) return;
int curSum = preSum + head.val;
if (!map.containsKey(curSum)) {
map.put(curSum, level);
}
if (map.containsKey(curSum-target)) {
maxLen = Math.max(maxLen, level-map.get(curSum-target));
}
preOrder(head.left, target, curSum, level+1, map);
preOrder(head.right, target, curSum, level+1, map);
if (level == map.get(curSum)) {
map.remove(curSum);
}
}
}
可以看到该题的解决思路是:第34题 + 最长子数组长度问题 的结合
38题是求全排列的。
求整个字符串的排列可以看成两步。第一步:求所有可能出现在第一个位置的字符,即把第一个字符与后面所有字符交换。第二步:固定第一个字符,求后面所有字符的排列。这时候仍然把后面字符分成两部分,:后面字符的第一个字符,以及该字符后的所有字符,可以看出这是典型的递归思路。
public List<List<Character>> recon2(String s) {
if (s == null || s.length() == 0) return null;
List<List<Character>> result = new ArrayList<>();
recon2(result, s.toCharArray(), 0);
return result;
}
private void recon2(List<List<Character>> result, char[] chas, int start) {
if (start == chas.length-1) result.add(asList(chas));
else {
for (int i = start; i < chas.length; i++) {
swap(chas, start, i);
recon2(result, chas, start+1);
swap(chas,start, i);
}
}
}
private List<Character> asList(char[] chas) {
List<Character> list = new ArrayList<>();
for (Character c : chas) {
list.add(c);
}
return list;
}
private void swap(char[] chas, int i, int j) {
char temp = chas[i];
chas[i] = chas[j];
chas[j] = temp;
}
另一种解法, 用list来过滤重复字符。
public List<List<Character>> permute(String s) {
if (s == null || s.length() == 0) return null;
List<List<Character>> result = new ArrayList<>();
recon(result, new ArrayList<>(), s.toCharArray());
return result;
}
private void recon(List<List<Character>> result, List<Character> list, char[] chas) {
if (list.size() == chas.length) result.add(list);
else {
for (int i = 0; i < chas.length; i++) {
if (list.contains(chas[i])) continue;
list.add(chas[i]);
recon(result, list, chas);
list.remove(list.size()-1);
}
}
}
拓展:求字符串的所有组合,如“abc” :a, b, c, ab, ac, bc, abc
n个字符组成长度为m的组合的问题分解为两个子问题:包含该字符情况下剩下字符组成x长度(m - 已组成的字符个数)的组合 ;不包含该字符情况下剩下字符组成x长度(m - 已组成的字符个数)的组合
public class Offer38Extand {
public static void combination(char[] arr, int start, int num,
ArrayList<Character> list) {
// assert arr != null && arr.length != 0;
if (num == 0) {
System.out.print(list + " ");
return;
}
if (start >= arr.length) return;
list.add(arr[start]);
combination(arr, start+1, num-1, list); //包含arr[start]字符
list.remove(list.size()-1);
combination(arr, start+1, num, list); //不包含arr[start]字符
}
public static void main(String[] args) {
char[] arr = {'a', 'b', 'c', 'd'};
for (int i = 1; i < 5; i++) {
System.out.println("组合大小为" + i);
combination(arr, 0, i, new ArrayList<>());
System.out.println();
}
}
}
还有另一种解法
public static void combination2(List<List<Character>> result,
List<Character> list, char[] arr, int start) {
result.add(new ArrayList<>(list));
for (int i = start; i < arr.length; i++) {
list.add(arr[i]);
combination2(result, list, arr, i+1); //注意这里是i+1
list.remove(list.size()-1);
}
}
这是一种求子集的解法,它包括了空集
问题二:求数组所有子集,包括空集。LeetCode78题。
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
recursive(result, new ArrayList<>(), nums, 0);
return result;
}
private void recursive(List<List<Integer>> result, List<Integer> list, int[] nums, int start){
result.add(new ArrayList<>(list));
for(int i = start; i < nums.length; i++){
list.add(nums[i]);
//这里是i+1;因为我们要的是子集,不像全序列,让递归推进下去
recursive(result, list, nums, i+1);
list.remove(list.size()-1);
}
}
重点就在于递归下一次下标是 i+1
问题三:仍是全排列的问题,不过有重复元素。即LeetCode47
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
boolean[] used = new boolean[nums.length]; // user[i]表明nums[i]已经加进list
Arrays.sort(nums); //这里排序是必须的
recursive(result, new ArrayList<>(), nums, used);
return result;
}
private void recursive(List<List<Integer>> result, List<Integer> list, int[] nums, boolean[] used){
if(list.size() == nums.length){
result.add(new ArrayList(list));
return;
}
for(int i = 0; i < nums.length; i++){
//若当前i位置元素没有加进list,其与i-1位置元素相同,
//检查i-1位置是否加入到list,若没有则说明其曾经加入到list,
//所有情况都被统计了,那么此时i元素的加入及统计它的组合情况既是重复
//若i-1位置加入了list,那么说明在统计i-1的相关组合情况,i位置的重复也是一种情况,应该被考虑进来
if(used[i] || i > 0 && nums[i-1] == nums[i] && !used[i-1]) continue;
list.add(nums[i]);
used[i] = true;
recursive(result, list, nums, used);
list.remove(list.size()-1);
used[i] = false;
}
}
另一种解法:
public List<List<Integer>> permuteUnique2(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
backtrack(result, nums, 0);
return result;
}
private void backtrack(List<List<Integer>> result, int[] nums, int start) {
if (start == nums.length-1) {
result.add(asList(nums));
}
else {
for (int i = start; i < nums.length; i++) {
boolean flag = false;
for (int j = i-1; j >= start; j--) {
if (nums[j] == nums[i]) {
flag = true;
break;
}
}
if (flag) continue;
swap(nums, start, i);
backtrack(result, nums, start+1);
swap(nums, start, i);
}
}
}
private List<Integer> asList(int[] nums) {
List<Integer> list = new ArrayList<>(nums.length);
for (int num : nums) {
list.add(num);
}
return list;
}
private void swap(int[] nums, int i, int j) {
if (i != j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
该题就是在38题全排列的基础上加上相关验证而已。
接下来看看全排列相关的其它题目,比如:
输入一个含有8个数字的的数组,判断有没有可能这8个数字分别放在正方体的8个顶点上,使得正方体三组相对的面上的四个顶点的和相等。
解决方法就是利用全排列找出所有组合方式,在验证每种组合是否满足限制条件。
还有一个全排列的问题:N皇后问题:N*N棋盘上要摆上N个皇后,要求不同行不同列且不再一条斜线上,问有多少中摆法? LeetCode52
/**
* N皇后问题
*/
public class Page238 {
public int solve(int n) {
assert n > 0;
int[] arr = new int[n];
return process(arr, 0, n);
}
/**
* arr 下标代表行,存储元素代表列,arr便代表N*N的棋盘。
* 问题分两部分来看:当前行的列选择及之后部分的选择。
* @param arr 代表N*N的棋盘
* @param i 代表当前行
* @param n 数组大小
* @return 返回结果
*/
private int process(int[] arr, int i, int n) {
if (i == n) return 1;
int res = 0;
for (int j = 0; j < n; j++) {
if (isValid(arr, i, j)) {
arr[i] = j;
res += process(arr, i+1, n);
}
}
return res;
}
/**
* 判断在ar[i]处放j是否符合规则:不与前面行所放元素在同一列,且不在同一斜线
* @param arr 下标代表行,存储元素代表列
* @param i 第i行
* @param j 第j列
* @return 返回true代表在i行放在j列可以
*/
private boolean isValid(int[] arr, int i, int j) {
for (int k = 0; k < i; k++) {
if (arr[k] == j || Math.abs(k-i) == Math.abs(arr[k]-j))
return false;
}
return true;
}
}
接下来对算法进行优化,采用位运算。
public int solve2(int n) {
assert n > 0 && n <= 32;
int upperLim = n == 32 ? -1 : (1 << n)-1;
return process(upperLim, 0, 0, 0);
}
/**
* 变量pos表示在colLim, leftDiaLim, rightDiaLim 这三个状态的影响下还有哪些位置是可以选择的,
* 1代表可以选择。mostRightOne表示最右边的1在什么位置。
* @param upperLim 该值表示棋盘已放满情况, 递归过程值不变
* @param colLim 表示递归到上一行为止,在哪些列上已经放置了皇后,1表示放置
* @param leftDiaLim 表示递归到上一行为止,因为受已经放置的所有皇后的左下方斜线影响,
* 导致当前行不能放置皇后。1表示不能放置
* @param rightDiaLim 表示递归到上一行为止,因为受已经放置的所有皇后的右下方斜线影响,
* 导致当前行不能放置皇后。1表示不能放置
* @return 返回结果
*/
private int process(int upperLim, int colLim, int leftDiaLim, int rightDiaLim) {
if (colLim == upperLim) return 1;
int pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
int mostRightOne = 0;
int res = 0;
while (pos != 0) {
mostRightOne = pos & (~pos + 1);
pos = pos - mostRightOne;
res += process(upperLim, colLim | mostRightOne,
(leftDiaLim | mostRightOne) << 1,
(rightDiaLim | mostRightOne) >>> 1);
}
return res;
}
即LeetCode91
public static int solve(String s) {
if (s == null || s.length() == 0) return -1;
char[] arr = s.toCharArray();
return process(arr, 0);
}
private static int process(char[] arr, int index) {
if (index >= arr.length) return 1;
if (arr[index] == '0') return 0;
if (index+1 < arr.length && arr[index+1] == '0') {
if (arr[index]-'0' > 2 || index+2 < arr.length && arr[index+2] == '0') return 0;
else return process(arr, index+2);
}
int res = 0;
res += process(arr, index+1);
if (index+1 < arr.length && arr[index]-'0' <= 2
&& (arr[index]-'0')*10+(arr[index+1]-'0') <= 26) {
res += process(arr, index+2);
}
return res;
}
动态规划:
public static int solve2(String s) {
if (s == null || s.length() == 0) return -1;
int ln = s.length();
int[] dp = new int[ln+1];
dp[ln] = 1;
dp[ln-1] = s.charAt(ln-1) == '0' ? 0 : 1;
for (int i = ln-2; i >= 0; i--) {
if (s.charAt(i) == '0') dp[i] = 0;
else {
if (Integer.parseInt(s.substring(i, i+2)) <= 26)
dp[i] = dp[i+1] + dp[i+2];
else
dp[i] = dp[i+1];
}
}
return dp[0];
}