zoukankan      html  css  js  c++  java
  • 《算法》第五章部分程序 part 5

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,Knuth-Morris-Pratt 无回溯匹配,Boyer - Moore 无回溯匹配,Rabin - Karp 指纹匹配

    ● Knuth-Morris-Pratt 无回溯匹配

     1 package package01;
     2 
     3 import edu.princeton.cs.algs4.StdOut;
     4 
     5 public class class01
     6 {
     7     private final int R;                    // 字符集基数
     8     private int[][] dfa;                    // 回溯数组
     9     private String pat;                     // 模式的两种保存方式
    10     private char[] pattern;
    11 
    12     public class01(String pat)              // 分别从两种表示方式中创建回溯数组,算法仙童
    13     {
    14         this.R = 256;
    15         this.pat = pat;
    16 
    17         int m = pat.length();
    18         dfa = new int[R][m];
    19         dfa[pat.charAt(0)][0] = 1;          // 第 0 位匹配,接下来匹配第 1 位
    20         for (int x = 0, j = 1; j < m; j++)  // 跳转点初始化为 0,即某次比较后需要退回到模式的头部再开始
    21         {
    22             for (int c = 0; c < R; c++)
    23                 dfa[c][j] = dfa[c][x];      // 原文字母 c 与模式第 j 位比较等价于原文字母 c 与模式第 x 位比较,跳转点也相同
    24             dfa[pat.charAt(j)][j] = j + 1;  // 原文某位与模式第 j 位正确匹配,接下来应该用原文下一位与模式 j+1 位进行匹配
    25             x = dfa[pat.charAt(j)][x];      // 设置 x 为原文某位与模式第 x 位比较后的跳转点
    26         }
    27     }
    28 
    29     public class01(char[] pattern, int R)
    30     {
    31         this.R = R;
    32         this.pattern = new char[pattern.length];
    33         for (int j = 0; j < pattern.length; j++)
    34             this.pattern[j] = pattern[j];
    35 
    36         int m = pattern.length;
    37         dfa = new int[R][m];
    38         dfa[pattern[0]][0] = 1;
    39         for (int x = 0, j = 1; j < m; j++)
    40         {
    41             for (int c = 0; c < R; c++)
    42                 dfa[c][j] = dfa[c][x];
    43             dfa[pattern[j]][j] = j + 1;
    44             x = dfa[pattern[j]][x];
    45         }
    46     }
    47 
    48     public int search(String txt)                       // 利用 DFA 原理进行匹配
    49     {
    50         int m = pat.length(), n = txt.length(), i, j;   // i 指向待匹配字符串,j 指向模式
    51         for (i = 0, j = 0; i < n && j < m; i++)         // i 不断自增,j 不断跳转
    52             j = dfa[txt.charAt(i)][j];                  // y = dfa[txt[i]][j] 表示 txt[i] 与 pattern[j] 比较后,下一步应该与 txt[i+1] 进行比较的模式的下标,
    53                                                         // 即下一步应该把 txt[i+1] 与 pattern[y] 进行比较
    54         return (j == m) ? (i - m) : n;                  // 找到匹配则返回子字符串的索引位置,没有匹配则返回原字符串的长度
    55     }
    56 
    57     public int search(char[] text)
    58     {
    59         int m = pattern.length;
    60         int n = text.length;
    61         int i, j;
    62         for (i = 0, j = 0; i < n && j < m; i++)
    63             j = dfa[text[i]][j];
    64         return (j == m) ? (i - m) : n;
    65     }
    66 
    67     public static void main(String[] args)
    68     {
    69         String pat = args[0], txt = args[1];
    70         char[] pattern = pat.toCharArray(), text = txt.toCharArray();   // 输入存成两种格式,分别进行匹配
    71 
    72         class01 kmp1 = new class01(pat);
    73         int offset1 = kmp1.search(txt);
    74         class01 kmp2 = new class01(pattern, 256);
    75         int offset2 = kmp2.search(text);
    76 
    77         StdOut.println("text:    " + txt);                              // 输出原文
    78         StdOut.print("pattern: ");
    79         for (int i = 0; i < offset1; i++)                               // 匹配的前导空格
    80             StdOut.print(" ");
    81         StdOut.println(pat);
    82 
    83         StdOut.print("pattern: ");
    84         for (int i = 0; i < offset2; i++)
    85             StdOut.print(" ");
    86         StdOut.println(pat);
    87     }
    88 }

    ● Boyer - Moore 无回溯匹配

     1 package package01;
     2 
     3 import edu.princeton.cs.algs4.StdOut;
     4 
     5 public class class01
     6 {
     7     private final int R;
     8     private int[] right;               // 回溯控制数组
     9     private char[] pattern;
    10     private String pat;
    11 
    12     public class01(String inputPat)
    13     {
    14         R = 256;
    15         pat = inputPat;
    16         int m = pat.length();
    17         right = new int[R];
    18         for (int c = 0; c < R; c++)   // 初始化为 -1
    19             right[c] = -1;
    20         for (int j = 0; j < m; j++)   // 记录每个字母在模式或者能够出现的最靠右的地方
    21             right[pat.charAt(j)] = j;
    22     }
    23 
    24     public class01(char[] inputPattern, int inputR)
    25     {
    26         R = inputR;
    27         pattern = new char[inputPattern.length];
    28         for (int j = 0; j < pattern.length; j++)
    29             pattern[j] = inputPattern[j];
    30         int m = pattern.length;
    31         right = new int[R];
    32         for (int c = 0; c < R; c++)
    33             right[c] = -1;
    34         for (int j = 0; j < m; j++)
    35             right[pattern[j]] = j;
    36     }
    37 
    38     public int search(String txt)
    39     {
    40         int m = pat.length(), n = txt.length(), skip;
    41         for (int i = 0; i <= n - m; i += skip)          // i 指向原文,向后移动
    42         {
    43             skip = 0;
    44             for (int j = m - 1; j >= 0; j--)            // j 指向模式,向前移动
    45             {
    46                 if (pat.charAt(j) != txt.charAt(i + j)) // 若原文第 i+j 位 和模式第 j 位匹配,则自减 j 继续检查,若不匹配则要尝试跳转
    47                 {
    48                     skip = Math.max(1, j - right[txt.charAt(i + j)]);   // 考虑原文第 i+j 位在匹配串中最右出现的位置,
    49                     break;                                              // 若模式根本没有该字符,则 right[x] 等于 -1,skip 等于 1,原文前进 1 格,从模式头部开始重新匹配                                                                        
    50                 }                                                       // 若模式有该字符,则 j - right[x] 等于该字符的右边的字符数(跳转后不需要检查的部分)
    51             }
    52             if (skip == 0)                              // j 循环检查完了 skip 都没变过,找到了,此时 i 指向原文中匹配的子字符串的开头
    53                 return i;
    54         }
    55         return n;                                       // i 循环走完了都没找到
    56     }
    57 
    58     public int search(char[] text)
    59     {
    60         int m = pattern.length, n = text.length, skip;
    61         for (int i = 0; i <= n - m; i += skip)
    62         {
    63             skip = 0;
    64             for (int j = m - 1; j >= 0; j--)
    65             {
    66                 if (pattern[j] != text[i + j])
    67                 {
    68                     skip = Math.max(1, j - right[text[i + j]]);
    69                     break;
    70                 }
    71             }
    72             if (skip == 0)
    73                 return i;
    74         }
    75         return n;
    76     }
    77 
    78     public static void main(String[] args)
    79     {
    80         String pat = args[0], txt = args[1];
    81         char[] pattern = pat.toCharArray(), text = txt.toCharArray();
    82 
    83         class01 bm1 = new class01(pat);
    84         int offset1 = bm1.search(txt);
    85         class01 bm2 = new class01(pattern, 256);
    86         int offset2 = bm2.search(text);
    87 
    88         StdOut.println("text:    " + txt);
    89         StdOut.print("pattern: ");
    90         for (int i = 0; i < offset1; i++)
    91             StdOut.print(" ");
    92         StdOut.println(pat);
    93 
    94         StdOut.print("pattern: ");
    95         for (int i = 0; i < offset2; i++)
    96             StdOut.print(" ");
    97         StdOut.println(pat);
    98     }
    99 }

    ● Rabin - Karp 指纹匹配

     1 package package01;
     2 
     3 import java.math.BigInteger;
     4 import java.util.Random;
     5 import edu.princeton.cs.algs4.StdOut;
     6 
     7 public class class01
     8 {
     9     private String pat;
    10     private long patHash;    // 模式的 hash 值
    11     private int R;           // 字符集基数
    12     private int m;           // 模式畅读
    13     private long q;          // 计算 hash 用的大素数
    14     private long RM;         // 中间值,等于 R^(m-1) % q
    15 
    16     public class01(char[] inputPattern, int inputR)
    17     {
    18         new class01(String.valueOf(inputPattern), inputR);
    19     }
    20 
    21     public class01(String inputPat, int inputR)
    22     {
    23         pat = inputPat;
    24         R = inputR;
    25         m = pat.length();
    26         q = longRandomPrime();
    27         RM = 1;                             // 计算 RM
    28         for (int i = 1; i <= m - 1; i++)
    29             RM = (R * RM) % q;
    30         patHash = hash(pat, m);
    31     }
    32 
    33     private long hash(String key, int m)    // 计算 hash 值,m 为字符串长度,hash = (Σ key[i] * R^(m-1-i)) % q,求和循环 i
    34     {                                       // 递推 x[0] = key[0] % q,x[k+1] = (R * x[k] + key[k+1]) % q
    35         long h = 0;
    36         for (int j = 0; j < m; j++)
    37             h = (R * h + key.charAt(j)) % q;
    38         return h;
    39     }
    40 
    41     public int search(String txt)
    42     {
    43         int n = txt.length();
    44         if (n < m)                                                      // 原文太短
    45             return n;
    46         long txtHash = hash(txt, m);                                    // 注意传入模式长度 m,表示计算 txt[0] ~ txt[m-1] 的 hash
    47         if ((patHash == txtHash) && check(txt, 0))                      // 原文头一段 hash 等于模式 hash,且通过了逐字比较,完成匹配
    48             return 0;
    49         for (int i = m; i < n; i++)
    50         {
    51             txtHash = (txtHash - RM * txt.charAt(i - m) % q + q) % q;   // 更新原文的 hash,首先去掉第 i - m 位
    52             txtHash = (txtHash*R + txt.charAt(i)) % q;                  // 然后加入第 i 位
    53             int offset = i - m + 1;                                     // 新的比较起点
    54             if ((patHash == txtHash) && check(txt, offset))             // 同上,通过 hash 比较和逐字比较,完成匹配
    55                 return offset;
    56         }
    57         return n;                                                       // 没有匹配
    58     }
    59 
    60     private static long longRandomPrime()                               // 生成大素数
    61     {
    62         return BigInteger.probablePrime(31, new Random()).longValue();
    63     }
    64 
    65     private boolean check(String txt, int i)                            // 复查原文 txt[i] ~ txt[i+m-1] 是否匹配模式
    66     {
    67         for (int j = 0; j < m; j++)
    68         {
    69             if (pat.charAt(j) != txt.charAt(i + j))
    70                 return false;
    71         }
    72         return true;
    73     }
    74 
    75     public static void main(String[] args)
    76     {
    77         String pat = args[0], txt = args[1];
    78         class01 searcher = new class01(pat, 256);
    79         int offset = searcher.search(txt);
    80 
    81         StdOut.println("text:    " + txt);
    82         StdOut.print("pattern: ");
    83         for (int i = 0; i < offset; i++)
    84             StdOut.print(" ");
    85         StdOut.println(pat);
    86     }
    87 }
  • 相关阅读:
    学习笔记 MYSQL报错注入(count()、rand()、group by)
    学习笔记 HTTP参数污染注入
    学习笔记 MSSQL显错手工注入
    代码审计入门后审计技巧
    字符串的排列
    二叉搜索树与双向链表
    复杂链表的复制
    二叉树中和为某一值的路径
    二叉搜索树的后序遍历序列
    从上往下打印二叉树
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/9869324.html
Copyright © 2011-2022 走看看