Some basic ideas of classic problems/algorithms.
BinarySearch 
2020更新:根据这个算法总结了一个不那么绕的二分查找 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 int  binarySearch (int [] nums, int  target)  {    if  (nums == null  || nums.length == 0 ) {         return  -1 ;     }     int  left  =  0 ;     int  right  =  nums.length - 1 ;     while  (left <= right) {              int  mid  =  left + (right - left) / 2 ;         if  (nums[mid] == target) {             return  nums[mid];         } else  if  (nums[mid] < target) {             left = mid + 1 ;              } else  if  (nums[mid] > target) {             right = mid - 1 ;             }     }     return  -1 ; } int  binarySearchFirst (int [] nums, int  target)  {    if  (nums == null  || nums.length == 0 ) {         return  -1 ;     }     int  left  =  0 ;     int  right  =  nums.length;         while  (left < right) {               int  mid  =  left + (right - left) / 2 ;         if  (nums[mid] == target) {             right = mid;             } else  if  (nums[mid] < target) {             left = mid + 1 ;          } else  if  (nums[mid] > target) {             right = mid;             }     }     return  left;      } int  binarySearchLast (int [] nums, int  target)  {    if  (nums == null  || nums.length == 0 ) {         return  -1 ;     }     int  left  =  0 ;     int  right  =  nums.length;         while  (left < right) {               int  mid  =  left + (right - left) / 2 ;         if  (nums[mid] == target) {             left = mid + 1 ;             } else  if  (nums[mid] < target) {             left = mid + 1 ;          } else  if  (nums[mid] > target) {             right = mid;             }     }     return  left - 1 ;          } 
Deprecated 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 private  int  binarySearchInsertionPos (int [] nums, int  target)  {    int  start  =  -1 , end = nums.length;     while  (end - start > 1 ) {         int  mid  =  start + (end - start) / 2 ;                  if  (nums[mid] >= target) {             end = mid;         } else  {             start = mid;         }     }     return  end; } private  int  binarySearchSqrt (int  x)  {    if  (x < 1 ) {         return  0 ;     }     long  start  =  -1 , end = (long )x + 1 ;     while  (end - start > 1 ) {         long  mid  =  start + (end - start) / 2 ;                  if  (mid > x / mid) {             end = mid;         } else  {             start = mid;         }     }     return  (int )start; } private  int  binarySearchFirst (int [] nums, int  target)  {    int  start  =  -1 , end = nums.length;     while  (end - start > 1 ) {         int  mid  =  start + (end - start) / 2 ;                  if  (nums[mid] >= target) {             end = mid;         } else  {             start = mid;         }     }      return  end < nums.length && nums[end] == target ? end : -1 ; } private  int  binarySearchLast (int [] nums, int  target)  {    int  start  =  -1 , end = nums.length;     while  (end - start > 1 ) {         int  mid  =  start + (end - start) / 2 ;                  if  (nums[mid] <= target) {             start = mid;         } else  {             end = mid;         }     }     return  start >= 0  && nums[start] == target ? start : -1 ; } 
Sort Selection Sort  外层循环i从头到尾、内层循环从i往后,每次找最小值所在的index,内层循环结束后和头部swap,这样每次都可以把当前最小值放到最前面。1 2 3 4 5 6 7 8 9 10 11 private  void  selectionSort (int [] arr)  {    for  (int  i  =  0 ; i < arr.length; i++) {         int  index  =  i;         for  (int  j  =  i + 1 ; j < arr.length; j++) {             if  (arr[j] < arr[index]) {                 index = j;             }         }         swap(arr, i, index);     } } 
Insertion Sort  外层循环i从前往后遍历,内层循环从i往前,每次将相邻的顺序反了的元素swap。1 2 3 4 5 6 7 private  void  insertionSort (int [] arr)  {    for  (int  i  =  0 ; i < arr.length; i++) {         for  (int  j  =  i; j > 0  && arr[j] < arr[j - 1 ]; j--) {             swap(arr, j, j - 1 );         }     } } 
Shell Sort  插入排序的升级版,循环的interval是从大到小的。排序的本质是消除逆序对,如果增大interval,则一次swap可能会消除更多的逆序对,因此可以突破O(N^2).1 2 3 4 5 6 7 8 9 10 11 12 13 14 private  void  shellSort (int [] arr)  {    int  h  =  1 ;     while  (h < arr.length / 3 ) {         h = 3  * h + 1 ;     }     while  (h >= 1 ) {         for  (int  i  =  h; i < arr.length; i++) {             for  (int  j  =  i; j >= h && arr[j] < arr[j - h]; j -= h) {                 swap(arr, j, j - h);             }         }         h /= 3 ;     } } 
Bubble Sort  外层循环从头到尾,内层循环从尾到i + 1不断把较小值放到前面。1 2 3 4 5 6 7 8 9 private  void  bubbleSort (int [] arr)  {    for  (int  i  =  0 ; i < arr.length; i++) {         for  (int  j  =  arr.length - 1 ; j > i; j--) {             if  (arr[j] < arr[j - 1 ]) {                 swap(arr, j, j - 1 );             }         }     } } 
Bucket Sort  木桶排序牺牲了空间换取速度,达到了O(N)。统计每个值出现的个数,然后依次放回原数组。1 2 3 4 5 6 7 8 9 10 11 12 13 private  void  bucketSort (int [] arr)  {    int  bucket  =  new  int  [1000010 ];     for  (int  i  =  0 ; i < arr.length; i++) {         bucket[arr[i]]++;     }     int  j  =  0 , k = 0 ;     while  (j < arr.length && k < bucket.length) {         while  ((bucket[k]--) > 0 ) {             arr[j++] = k;         }         k++;     } } 
Radix Sort[====TODO====]  基数排序就是根据每个位数字大小,从个位数、十位数、百位数、千位数…存入bucket1 2 3 4 5 6 7 8 9 10 private  void  radixSort (int [] arr, int  radix, int  maxLen)  {    int [] temp = new  int  [arr.length];     int [] bucket = new  int  [radix];     for  (int  i  =  0 , devide = 1 ; i < maxLen; i++) {         Arrays.fill(bucket, 0 );         System.arraycopy(arr, 0 , temp, 0 , arr.length);     } } 
Merge Sort  分治法思想,将两个序列不断拆分,拆到不能再拆后分别排序后合并。归并排序(以及插入排序)是稳定的。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  void  mergeSort (int [] arr)  {    partition(arr, 0 , arr.length - 1 ); } private  void  partition (int [] arr, int  start, int  end)  {    if  (start >= end) {         return ;     }     int  mid  =  (start + end) / 2 ;     partition(arr, start, mid);     partition(arr, mid + 1 , end);     if  (arr[mid] < arr[mid + 1 ]) {         return ;     }     merge(arr, start, mid, end); } private  void  merge (int [] arr, int  start, int  mid, int  end)  {    int  left  =  start, right = mid + 1 , index = start;     int [] temp = new  int  [arr.length];     while  (left <= mid && right <= end) {         if  (arr[left] < arr[right]) {             temp[index++] = arr[left++];         } else  {             temp[index++] = arr[right++];         }     }     while  (left <= mid) {         temp[index] = arr[left++];     }     while  (right <= end) {         temp[index] = arr[right++];     }     for  (index = start; index <= end; index++) {         arr[index] = temp[index];     } } 
Quick Sort + Shuffle  定义一个pivot值,找到它的位置使得左边都小于等于它、右边都大于等于它,然后再递归到两边去排。最差情况出现在当已经有序的时候,退化成O(N^2),因为每个pivot都要和后面的比较一波。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 private  void  shuffle (int [] arr)  {    for  (int  i  =  0 ; i < arr.length; i++) {         swap(arr, i, Random.nextInt(i + 1 ));     } } private  void  quickSort (int [] arr)  {    quickSort(arr, 0 , arr.length - 1 ); } private  void  quickSort (int [] arr, int  start, int  end)  {    if  (start >= end) {         return ;     }     int  j  =  partition(arr, start, end);     quickSort(arr, start, j - 1 );     quickSort(arr, j + 1 , end); } private  int  partition (int [] arr, int  start, int  end)  {    int  left  =  start, right = end + 1 , pivot = arr[start];     while  (true ) {         while  (arr[++left] < pivot) {             if  (left == end) {                 break ;             }         }         while  (arr[--right] > pivot) {             if  (right == start) {                 break ;             }         }         if  (left >= right) {             break ;         }         swap(arr, left, right);     }     swap(arr, start, right);      return  right; } 
Big Integer Calculation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class  Multiply  {    public  String multiply (String num1, String num2)  {         if  (num1 == null  || num2 == null             || num1.length() == 0  || num2.length() == 0 ) {             return  "" ;         }         int  m  =  num1.length(), n = num2.length();         int [] result = new  int  [m + n];         char [] cnum1 = num1.toCharArray(), cnum2 = num2.toCharArray();                           for  (int  i  =  m - 1 ; i >= 0 ; i--) {             for  (int  j  =  n - 1 ; j >= 0 ; j--) {                 int  mult  =  (cnum1[i] - '0' ) * (cnum2[j] - '0' );                 int  first  =  i + j, second = i + j + 1 ;                 int  sum  =  mult + result[second];                     result[first] += sum / 10 ;                   result[second] = sum % 10 ;               }         }                  StringBuilder  sb  =  new  StringBuilder ();         int  i  =  0 ;                  while  (i < result.length && sb.length() == 0  && result[i] == 0 ) {             i++;         }         while  (i < result.length) {             sb.append(result[i++]);         }         return  sb.length() == 0 ? "0" : sb.toString();     } } class  Power  {    public  double  myPow (double  x, int  n)  {         if  (n == 0  || x == 1 ) {             return  1 ;         }         if  (n == Integer.MIN_VALUE) {                return  (1.0  / x) * myPow(x, n + 1 );         }         if  (n < 0 ) {             n = -n;             x = 1.0  / x;         }         return  (n & 1 ) != 0  ? x * myPow(x * x, n / 2 ) : myPow(x * x, n / 2 );      } } 
GCD 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  int  findGcd (int  a, int  b)  {    while  (b != 0 ) {         int  t  =  a;         a = b;         b = t % b;     }     return  a; } public  int  findGcd (int  a, int  b)  {    if  (b > a) {         return  findGcd(b, a);     }     return  b == 0  ? a : findGcd(b, a % b); } 
Union Find  并查集:在UF类中维护一个id数组,这个id表示当前索引的祖宗的索引,若id[idx] = idx说明它本身就是老大,初始时每个元素都是自己的老大。find(p)函数就是返回p的老大的索引,union(p, q)函数就是将索引p和q两伙合并起来,分别找到p和q的老大索引pRoot和qRoot,但是他们谁隶属于谁呢?rank就是在这时候起作用,将rank高的保留,rank低的老大合并到更高的老大那去,若两个老大一样,就随意了,合并后新老大的rank要加加。在UF中还有一个count变量,其实就是算有几个独立老大的,初始时每个1都是老大,而每次成功调用union时count都会减减。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class  UF  {    private  int [] id = null ;     private  int [] rank = null ;     private  int  count  =  0 ;     public  UF (char [][] grid)  {         int  rows  =  grid.length, cols = grid[0 ].length;         id = new  int  [rows * cols];         rank = new  int  [rows * cols];                  for  (int  i  =  0 ; i < rows; i++) {             for  (int  j  =  0 ; j < cols; j++) {                 if  (grid[i][j] == '1' ) {                     count++;                 }                 int  temp  =  i * cols + j;                 id[temp] = temp;                 rank[temp] = 0 ;             }         }     }     public  int  find (int  p)  {         while  (id[p] != p) {             id[p] = id[id[p]];                p = id[p];         }         return  p;     }     public  void  union (int  p, int  q)  {         int  pRoot  =  find(p);         int  qRoot  =  find(q);         if  (pRoot == qRoot) {             return ;         }         if  (rank[pRoot] > rank[qRoot]) {             id[qRoot] = pRoot;           } else  if  (rank[qRoot] > rank[pRoot]) {             id[pRoot] = qRoot;         } else  {             id[pRoot] = qRoot;             rank[qRoot]++;               }         count--;     }     public  boolean  isConnected (int  p, int  q)  {         return  find(p) == find(q);     }     public  int  getCount ()  {         return  count;     } } 
https://leetcode.com/problems/number-of-islands/ 
Backtracking 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  List<List<Integer>> subsets (int [] nums)  {    List<List<Integer>> list = new  ArrayList <>();          backtrack(list, new  ArrayList <>(), nums, 0 );     return  list; } private  void  backtrack (List<List<Integer>> list , List<Integer> tempList, int  [] nums, int  start) {    list.add(new  ArrayList <>(tempList));     for (int  i  =  start; i < nums.length; i++){         tempList.add(nums[i]);         backtrack(list, tempList, nums, i + 1 );         tempList.remove(tempList.size() - 1 );     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  List<List<Integer>> subsetsWithDup (int [] nums)  {    List<List<Integer>> list = new  ArrayList <>();     Arrays.sort(nums);     backtrack(list, new  ArrayList <>(), nums, 0 );     return  list; } private  void  backtrack (List<List<Integer>> list, List<Integer> tempList, int  [] nums, int  start) {    list.add(new  ArrayList <>(tempList));     for (int  i  =  start; i < nums.length; i++){         if (i > start && nums[i] == nums[i - 1 ])              continue ;          tempList.add(nums[i]);         backtrack(list, tempList, nums, i + 1 );         tempList.remove(tempList.size() - 1 );     } }  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  List<List<Integer>> permute (int [] nums)  {   List<List<Integer>> list = new  ArrayList <>();        backtrack(list, new  ArrayList <>(), nums);    return  list; } private  void  backtrack (List<List<Integer>> list, List<Integer> tempList, int  [] nums) {   if  (tempList.size() == nums.length) {       list.add(new  ArrayList <>(tempList));    } else  {       for  (int  i  =  0 ; i < nums.length; i++) {           if  (tempList.contains(nums[i])) continue ;           tempList.add(nums[i]);          backtrack(list, tempList, nums);          tempList.remove(tempList.size() - 1 );       }    } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  List<List<Integer>> permuteUnique (int [] nums)  {    List<List<Integer>> list = new  ArrayList <>();     Arrays.sort(nums);     backtrack(list, new  ArrayList <>(), nums, new  boolean [nums.length]);     return  list; } private  void  backtrack (List<List<Integer>> list, List<Integer> tempList, int  [] nums, boolean  [] used) {    if  (tempList.size() == nums.length) {         list.add(new  ArrayList <>(tempList));     } else  {         for  (int  i  =  0 ; i < nums.length; i++) {             if  (used[i] || i > 0  && nums[i] == nums[i-1 ] && !used[i - 1 ]) continue ;             used[i] = true ;              tempList.add(nums[i]);             backtrack(list, tempList, nums, used);             used[i] = false ;              tempList.remove(tempList.size() - 1 );         }     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  Solution  {    private  void  dfs (List<List<Integer>> ans, List<Integer> arr, int  n, int  k, int  start)  {         if  (arr.size() == k) {             ans.add(new  ArrayList (arr));             return ;         }         for  (int  i  =  start; i <= n; i++) {             arr.add(i);             dfs(ans, arr, n, k, i+1 );             arr.remove(arr.size()-1 );         }     }     public  List<List<Integer>> combine (int  n, int  k)  {         List<List<Integer>> ans = new  ArrayList <List<Integer>>();         ArrayList<Integer> arr = new  ArrayList <Integer>();         if  (n <= 0 ) {             return  ans;         }         dfs(ans, arr, n, k, 1 );         return  ans;     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  List<List<Integer>> combinationSum (int [] nums, int  target)  {    List<List<Integer>> list = new  ArrayList <>();     Arrays.sort(nums);     backtrack(list, new  ArrayList <>(), nums, target, 0 );     return  list; } private  void  backtrack (List<List<Integer>> list, List<Integer> tempList, int  [] nums, int  remain, int  start) {    if  (remain < 0 ) return ;     else  if  (remain == 0 ) list.add(new  ArrayList <>(tempList));     else  {          for  (int  i  =  start; i < nums.length; i++){             tempList.add(nums[i]);             backtrack(list, tempList, nums, remain - nums[i], i);              tempList.remove(tempList.size() - 1 );         }     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  List<List<Integer>> combinationSum2 (int [] nums, int  target)  {    List<List<Integer>> list = new  ArrayList <>();     Arrays.sort(nums);     backtrack(list, new  ArrayList <>(), nums, target, 0 );     return  list; } private  void  backtrack (List<List<Integer>> list, List<Integer> tempList, int  [] nums, int  remain, int  start) {    if  (remain < 0 ) return ;     else  if  (remain == 0 ) list.add(new  ArrayList <>(tempList));     else  {         for  (int  i  =  start; i < nums.length; i++){             if  (i > start && nums[i] == nums[i - 1 ]) continue ;              tempList.add(nums[i]);             backtrack(list, tempList, nums, remain - nums[i], i + 1 );             tempList.remove(tempList.size() - 1 );          }     } }  
根据pattern构建有限状态机的DP数组,然后再取给定的一堆text中找匹配的索引。dp[j][c] = next表示,当前是状态j,遇到了字符c,应该转移到状态next. 需要一个辅助状态X,它永远比当前状态j落后一个状态,拥有和j最长的相同前缀,我们给它起了个名字叫「影子状态」. 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  KMP  {    private  int [][] dp;     private  String pattern;     public  KMP (String pattern)  {         this .pattern = pattern;         int  M  =  this .pattern.length()                  dp = new  int [][256 ];                  dp[0 ][this .pattern.charAt(0 )] = 1 ;                  int  X  =  0 ;         for  (int  j  =  1 ; j < M; j++) {             for  (int  c  =  0 ; c < 256 ; c++) {                 dp[j][c] = dp[X][c];                 }             dp[j][pattern.charAt(j)] = j + 1 ;                X = dp[X][pattern.charAt(j)];                }     }     public  int  search (String text)  {         int  M  =  pattern.length();         int  N  =  text.length();         int  j  =  0 ;         for  (int  i  =  0 ; i < N; i++) {                          j = dp[j][text.charAt(i)];             if  (j == M) {                                  return  i - M + 1 ;             }         }         return  -1 ;     } } 
此外再介绍leetcode模版。在构建pattern的dp数组时索引从1开始且单向前进,在text中搜索时索引从0开始且时单向前进。 
 
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class  Solution  {    public  int  strStr (String haystack, String needle)  {         if  (haystack == null  || needle == null  || needle.isEmpty()) {             return  0 ;         }         int  len  =  needle.length();         int [] dp = new  int [len];                 for  (int  i  =  1 ; i < len; i++) {             int  j  =  dp[i - 1 ];                       while  (j > 0  && needle.charAt(j) != needle.charAt(i)) {                 j = dp[j - 1 ];                       }             if  (needle.charAt(j) == needle.charAt(i)) {                 j++;             }             dp[i] = j;         }                  int  lenFull  =  haystack.length();         int  j  =  0 ;         for  (int  i  =  0 ; i < lenFull; i++) {             while  (j > 0  && haystack.charAt(i) != needle.charAt(j)) {                 j = dp[j - 1 ];             }             if  (haystack.charAt(i) == needle.charAt(j)) {                 j++;             }             if  (j == len) {                 return  i - len + 1 ;             }         }         return  -1 ;     } } 
Stack Tree https://leetcode.com/problems/boundary-of-binary-tree/ 
Preorder Iterative 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  List<Integer> preorderTraversal (TreeNode root)  {    List<Integer> output = new  LinkedList <>();     LinkedList<TreeNode> stack = new  LinkedList <>();     if  (root == null ) {         return  output;     }     stack.add(root);     while  (!stack.isEmpty()) {         TreeNode  node  =  stack.pollLast();         output.add(node.val);         if  (node.right != null ) {             stack.add(node.right);         }         if  (node.left != null ) {             stack.add(node.left);         }     }     return  output; } 
Inorder Iterative 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  List<Integer> inorderTraversal (TreeNode root)  {    List<Integer> ans = new  ArrayList <>();     if  (root == null ) {         return  ans;     }     Deque<TreeNode> s = new  ArrayDeque <>();     TreeNode  curr  =  root;     while  (curr != null  || !s.isEmpty()) {         while  (curr != null ) {             s.push(curr);             curr = curr.left;         }         curr = s.pop();         ans.add(curr.val);         curr = curr.right;     }     return  ans; } 
complete二叉树是指每一层都逐步从左到右填充节点,左右孩子高度差顶多为1. 计算有多少个节点在普通二叉树中是O(N),而在完美二叉树中节点数就是高度的二次幂,因此O(logN)求高度即可。而在完全二叉树中直接遍历比较蠢,就得想怎么利用高度做文章。如果左右孩子的高度一样,那就直接照搬完美二叉树;如果不同就继续按照O(N)递归。时间复杂度为O(logN * logN)(每次递归的复杂度为logN,总共只需要logN次递归,因为完全二叉树其中一个子树一定为完美二叉树,并不是每一次都会触发递归) 
 
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  int  countCompleteTreeNodes (TreeNode root)  {    TreeNode  l  =  root, r = root;     int  heightLeft  =  0 , heightRight = 0 ;     while  (l != null ) {         l = l.left;         heightLeft++;     }     while  (r != null ) {         r = r.right;         heightRight++;     }     if  (heightLeft == heightRight) {         return  (int )Math.pow(2 , heightLeft) - 1 ;     }     return  1  + countCompleteTreeNodes(root.left) + countCompleteTreeNodes(root.right); } 
Postorder Iterative 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  List<Integer> postorderTraversal (TreeNode root)  {    List<Integer> retVal = new  LinkedList <>();     if  (root == null ) {         return  retVal;     }     Deque<TreeNode> treeNodeStack = new  ArrayDeque <>();     treeNodeStack.push(root);     while  (!treeNodeStack.isEmpty()) {         TreeNode  curr  =  treeNodeStack.pop();         if  (curr.left != null ) {             treeNodeStack.push(curr.left);         }         if  (curr.right != null ) {             treeNodeStack.push(curr.right);         }         retVal.add(curr.val);     }     Collections.reverse(retVal);     return  retVal; } 
Trie 
String substring palindrome: 516, 647. https://leetcode.com/submissions/detail/121291766/ https://leetcode.com/submissions/detail/121292245/