▶ 书中第六章部分程序,包括在加上自己补充的代码,利用后缀树查找最长重复子串、查找最大重复子串并输出其上下文(Key word in context,KWIC)、求两字符串的最长公共子串
● 利用后缀树查找最长重复子串
1 package package01; 2 3 import edu.princeton.cs.algs4.StdIn; 4 import edu.princeton.cs.algs4.StdOut; 5 import edu.princeton.cs.algs4.SuffixArrayX; 6 7 public class class01 8 { 9 private class01() {} 10 11 public static String lrs(String text) 12 { 13 int n = text.length(); 14 SuffixArrayX sa = new SuffixArrayX(text); 15 String lrs = ""; 16 for (int i = 1; i < n; i++) // 遍历一次,记录最长公共前缀 17 { 18 int length = sa.lcp(i); 19 if (length > lrs.length()) 20 lrs = text.substring(sa.index(i), sa.index(i) + length); 21 } 22 return lrs; 23 } 24 25 public static void main(String[] args) 26 { 27 String text = StdIn.readAll().replaceAll("\s+", " "); // 空白字符全部换成 ' ' 28 StdOut.println("'" + lrs(text) + "'"); 29 } 30 }
● 利用后缀树查找最大重复子串并输出其上下文(Key word in context,KWIC)
1 package package01; 2 3 import edu.princeton.cs.algs4.In; 4 import edu.princeton.cs.algs4.StdIn; 5 import edu.princeton.cs.algs4.StdOut; 6 import edu.princeton.cs.algs4.SuffixArrayX; 7 8 public class class01 9 { 10 private class01() {} 11 12 public static void main(String[] args) 13 { 14 In in = new In(args[0]); // 命令参数,分别为输入文件和需要输出的上下文字符数量 15 int context = Integer.parseInt(args[1]); 16 String text = in.readAll().replaceAll("\s+", " "); 17 int n = text.length(); 18 SuffixArrayX sa = new SuffixArrayX(text); 19 20 for (; StdIn.hasNextLine(); StdOut.println()) 21 { 22 String query = StdIn.readLine(); 23 for (int i = sa.rank(query); i < n; i++) 24 { 25 int from1 = sa.index(i), to1 = Math.min(n, from1 + query.length()); // 取 index[i] 的头和尾,要求它和 query 不同 26 if (!query.equals(text.substring(from1, to1))) 27 break; 28 int from2 = Math.max(0, sa.index(i) - context), to2 = Math.min(n, sa.index(i) + context + query.length()); 29 StdOut.println(text.substring(from2, to2)); // 向前向后各取 context 个字符 30 } 31 } 32 } 33 }
● 利用后缀树求两字符串的最长公共子串
1 package package01; 2 3 import edu.princeton.cs.algs4.In; 4 import edu.princeton.cs.algs4.StdOut; 5 import edu.princeton.cs.algs4.SuffixArrayX; 6 7 public class class01 8 { 9 private class01() {} 10 11 private static String lcp(String s, int p, String t, int q) // 返回 s[p] 和 t[q] 开始的两个子串的最大公共子串 12 { 13 int n = Math.min(s.length() - p, t.length() - q); 14 for (int i = 0; i < n; i++) // 逐元素比较就好了,返回保持相等的最长子串 15 { 16 if (s.charAt(p + i) != t.charAt(q + i)) 17 return s.substring(p, p + i); 18 } 19 return s.substring(p, p + n); 20 } 21 22 private static int compare(String s, int p, String t, int q) // 比较两个后缀元素的字典序,用于判断哪个后缀元素要改用下一个 23 { 24 int n = Math.min(s.length() - p, t.length() - q); 25 for (int i = 0; i < n; i++) 26 { 27 if (s.charAt(p + i) != t.charAt(q + i)) 28 return s.charAt(p + i) - t.charAt(q + i); 29 } 30 if (s.length() - p < t.length() - q) 31 return -1; 32 else if (s.length() - p > t.length() - q) 33 return +1; 34 return 0; 35 } 36 37 public static String lcs(String s, String t) 38 { 39 SuffixArrayX suffix1 = new SuffixArrayX(s), suffix2 = new SuffixArrayX(t); 40 String lcs = ""; 41 for (int i = 0, j = 0; i < s.length() && j < t.length();) // 两个后缀数组比较 O(s.length() + t.length()) 次 42 { // 每次检查两个后缀元素的最长相等子串 43 int p = suffix1.index(i), q = suffix2.index(j); // 一旦找到不相等的元素,字典序靠前的元素就取下一个后缀元素继续比较 44 String x = lcp(s, p, t, q); 45 if (x.length() > lcs.length()) 46 lcs = x; 47 if (compare(s, p, t, q) < 0) 48 i++; 49 else 50 j++; 51 } 52 return lcs; 53 } 54 55 public static void main(String[] args) 56 { 57 In in1 = new In(args[0]), in2 = new In(args[1]); 58 String s = in1.readAll().trim().replaceAll("\s+", " "); 59 String t = in2.readAll().trim().replaceAll("\s+", " "); 60 StdOut.println("'" + lcs(s, t) + "'"); 61 } 62 }