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

● Knuth-Morris-Pratt 无回溯匹配

 package package01;

 import edu.princeton.cs.algs4.StdOut;

 public class class01
{
private final int R; // 字符集基数
private int[][] dfa; // 回溯数组
private String pat; // 模式的两种保存方式
private char[] pattern; public class01(String pat) // 分别从两种表示方式中创建回溯数组,算法仙童
{
this.R = 256;
this.pat = pat; int m = pat.length();
dfa = new int[R][m];
dfa[pat.charAt(0)][0] = 1; // 第 0 位匹配,接下来匹配第 1 位
for (int x = 0, j = 1; j < m; j++) // 跳转点初始化为 0,即某次比较后需要退回到模式的头部再开始
{
for (int c = 0; c < R; c++)
dfa[c][j] = dfa[c][x]; // 原文字母 c 与模式第 j 位比较等价于原文字母 c 与模式第 x 位比较,跳转点也相同
dfa[pat.charAt(j)][j] = j + 1; // 原文某位与模式第 j 位正确匹配,接下来应该用原文下一位与模式 j+1 位进行匹配
x = dfa[pat.charAt(j)][x]; // 设置 x 为原文某位与模式第 x 位比较后的跳转点
}
} public class01(char[] pattern, int R)
{
this.R = R;
this.pattern = new char[pattern.length];
for (int j = 0; j < pattern.length; j++)
this.pattern[j] = pattern[j]; int m = pattern.length;
dfa = new int[R][m];
dfa[pattern[0]][0] = 1;
for (int x = 0, j = 1; j < m; j++)
{
for (int c = 0; c < R; c++)
dfa[c][j] = dfa[c][x];
dfa[pattern[j]][j] = j + 1;
x = dfa[pattern[j]][x];
}
} public int search(String txt) // 利用 DFA 原理进行匹配
{
int m = pat.length(), n = txt.length(), i, j; // i 指向待匹配字符串,j 指向模式
for (i = 0, j = 0; i < n && j < m; i++) // i 不断自增,j 不断跳转
j = dfa[txt.charAt(i)][j]; // y = dfa[txt[i]][j] 表示 txt[i] 与 pattern[j] 比较后,下一步应该与 txt[i+1] 进行比较的模式的下标,
// 即下一步应该把 txt[i+1] 与 pattern[y] 进行比较
return (j == m) ? (i - m) : n; // 找到匹配则返回子字符串的索引位置,没有匹配则返回原字符串的长度
} public int search(char[] text)
{
int m = pattern.length;
int n = text.length;
int i, j;
for (i = 0, j = 0; i < n && j < m; i++)
j = dfa[text[i]][j];
return (j == m) ? (i - m) : n;
} public static void main(String[] args)
{
String pat = args[0], txt = args[1];
char[] pattern = pat.toCharArray(), text = txt.toCharArray(); // 输入存成两种格式,分别进行匹配 class01 kmp1 = new class01(pat);
int offset1 = kmp1.search(txt);
class01 kmp2 = new class01(pattern, 256);
int offset2 = kmp2.search(text); StdOut.println("text: " + txt); // 输出原文
StdOut.print("pattern: ");
for (int i = 0; i < offset1; i++) // 匹配的前导空格
StdOut.print(" ");
StdOut.println(pat); StdOut.print("pattern: ");
for (int i = 0; i < offset2; i++)
StdOut.print(" ");
StdOut.println(pat);
}
}

● Boyer - Moore 无回溯匹配

 package package01;

 import edu.princeton.cs.algs4.StdOut;

 public class class01
{
private final int R;
private int[] right; // 回溯控制数组
private char[] pattern;
private String pat; public class01(String inputPat)
{
R = 256;
pat = inputPat;
int m = pat.length();
right = new int[R];
for (int c = 0; c < R; c++) // 初始化为 -1
right[c] = -1;
for (int j = 0; j < m; j++) // 记录每个字母在模式或者能够出现的最靠右的地方
right[pat.charAt(j)] = j;
} public class01(char[] inputPattern, int inputR)
{
R = inputR;
pattern = new char[inputPattern.length];
for (int j = 0; j < pattern.length; j++)
pattern[j] = inputPattern[j];
int m = pattern.length;
right = new int[R];
for (int c = 0; c < R; c++)
right[c] = -1;
for (int j = 0; j < m; j++)
right[pattern[j]] = j;
} public int search(String txt)
{
int m = pat.length(), n = txt.length(), skip;
for (int i = 0; i <= n - m; i += skip) // i 指向原文,向后移动
{
skip = 0;
for (int j = m - 1; j >= 0; j--) // j 指向模式,向前移动
{
if (pat.charAt(j) != txt.charAt(i + j)) // 若原文第 i+j 位 和模式第 j 位匹配,则自减 j 继续检查,若不匹配则要尝试跳转
{
skip = Math.max(1, j - right[txt.charAt(i + j)]); // 考虑原文第 i+j 位在匹配串中最右出现的位置,
break; // 若模式根本没有该字符,则 right[x] 等于 -1,skip 等于 1,原文前进 1 格,从模式头部开始重新匹配
} // 若模式有该字符,则 j - right[x] 等于该字符的右边的字符数(跳转后不需要检查的部分)
}
if (skip == 0) // j 循环检查完了 skip 都没变过,找到了,此时 i 指向原文中匹配的子字符串的开头
return i;
}
return n; // i 循环走完了都没找到
} public int search(char[] text)
{
int m = pattern.length, n = text.length, skip;
for (int i = 0; i <= n - m; i += skip)
{
skip = 0;
for (int j = m - 1; j >= 0; j--)
{
if (pattern[j] != text[i + j])
{
skip = Math.max(1, j - right[text[i + j]]);
break;
}
}
if (skip == 0)
return i;
}
return n;
} public static void main(String[] args)
{
String pat = args[0], txt = args[1];
char[] pattern = pat.toCharArray(), text = txt.toCharArray(); class01 bm1 = new class01(pat);
int offset1 = bm1.search(txt);
class01 bm2 = new class01(pattern, 256);
int offset2 = bm2.search(text); StdOut.println("text: " + txt);
StdOut.print("pattern: ");
for (int i = 0; i < offset1; i++)
StdOut.print(" ");
StdOut.println(pat); StdOut.print("pattern: ");
for (int i = 0; i < offset2; i++)
StdOut.print(" ");
StdOut.println(pat);
}
}

● Rabin - Karp 指纹匹配

 package package01;

 import java.math.BigInteger;
import java.util.Random;
import edu.princeton.cs.algs4.StdOut; public class class01
{
private String pat;
private long patHash; // 模式的 hash 值
private int R; // 字符集基数
private int m; // 模式畅读
private long q; // 计算 hash 用的大素数
private long RM; // 中间值,等于 R^(m-1) % q public class01(char[] inputPattern, int inputR)
{
new class01(String.valueOf(inputPattern), inputR);
} public class01(String inputPat, int inputR)
{
pat = inputPat;
R = inputR;
m = pat.length();
q = longRandomPrime();
RM = 1; // 计算 RM
for (int i = 1; i <= m - 1; i++)
RM = (R * RM) % q;
patHash = hash(pat, m);
} private long hash(String key, int m) // 计算 hash 值,m 为字符串长度,hash = (Σ key[i] * R^(m-1-i)) % q,求和循环 i
{ // 递推 x[0] = key[0] % q,x[k+1] = (R * x[k] + key[k+1]) % q
long h = 0;
for (int j = 0; j < m; j++)
h = (R * h + key.charAt(j)) % q;
return h;
} public int search(String txt)
{
int n = txt.length();
if (n < m) // 原文太短
return n;
long txtHash = hash(txt, m); // 注意传入模式长度 m,表示计算 txt[0] ~ txt[m-1] 的 hash
if ((patHash == txtHash) && check(txt, 0)) // 原文头一段 hash 等于模式 hash,且通过了逐字比较,完成匹配
return 0;
for (int i = m; i < n; i++)
{
txtHash = (txtHash - RM * txt.charAt(i - m) % q + q) % q; // 更新原文的 hash,首先去掉第 i - m 位
txtHash = (txtHash*R + txt.charAt(i)) % q; // 然后加入第 i 位
int offset = i - m + 1; // 新的比较起点
if ((patHash == txtHash) && check(txt, offset)) // 同上,通过 hash 比较和逐字比较,完成匹配
return offset;
}
return n; // 没有匹配
} private static long longRandomPrime() // 生成大素数
{
return BigInteger.probablePrime(31, new Random()).longValue();
} private boolean check(String txt, int i) // 复查原文 txt[i] ~ txt[i+m-1] 是否匹配模式
{
for (int j = 0; j < m; j++)
{
if (pat.charAt(j) != txt.charAt(i + j))
return false;
}
return true;
} public static void main(String[] args)
{
String pat = args[0], txt = args[1];
class01 searcher = new class01(pat, 256);
int offset = searcher.search(txt); StdOut.println("text: " + txt);
StdOut.print("pattern: ");
for (int i = 0; i < offset; i++)
StdOut.print(" ");
StdOut.println(pat);
}
}

《算法》第五章部分程序 part 5的更多相关文章

  1. 《算法》第五章部分程序 part 3

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(美国国旗排序) ● 美国国旗排序 package package01; import edu.princeton.cs.algs4 ...

  2. 《算法》第五章部分程序 part 8

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,适用于基因序列的 2-Bit 压缩算法,行程长压缩算法,Huffman 压缩算法,LZW 压缩算法 ● 适用于基因序列的 2-Bit 压缩算法 pac ...

  3. 《算法》第五章部分程序 part 7

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串的二进制表示.十六进制表示.图形表示 ● 二进制表示 package package01; import edu.princeton.cs.al ...

  4. 《算法》第五章部分程序 part 6

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(NFA),grep 命令(利用 NFA 匹配) ● 非确定性有穷自动机(NFA) package package01; impor ...

  5. 《算法》第五章部分程序 part 4

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,Trie 树类,Trie 集合,三值搜索树(Ternary Search Trie) ● Trie 树类 package package01; imp ...

  6. 《算法》第五章部分程序 part 2

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(计数 + 插排),(原地排序),(三路快排,与前面的三路归并排序相同) ● 计数 + 插排 package package01; ...

  7. 《算法》第五章部分程序 part 1

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字母表类,字符串低位优先排序(桶排) ● 字母表类 package package01; import edu.princeton.cs.algs4. ...

  8. Gradle 1.12用户指南翻译——第四十五章. 应用程序插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. 《算法》第一章部分程序 part 1

    ▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...

随机推荐

  1. 在windows server 2012上安装.net3.5

    1.dism.exe /online /enable-feature /featurename:NetFX3 /Source:D:\sources\sxs /all 2.Start the Local ...

  2. 数据仓库专题(2)-Kimball维度建模四步骤

    一.前言 四步过程维度建模由Kimball提出,可以做为业务梳理.数据梳理后进行多维数据模型设计的指导流程,但是不能作为数据仓库系统建设的指导流程.本文就相关流程及核心问题进行解读. 二.数据仓库建设 ...

  3. spring AOP的学习

    1.Spring常用的概念 Joinpoint(连接点): 所谓连接点是指那些被拦截到的点.在spring中,这些点指的是方法,因为spring只支持方法类型的连接点. Pointcut(切入点): ...

  4. 【java】之深入理解JVM

    JVM规范定义的标准结构如下: 以上结构是JVM标准规范中定义的,但各厂家在实现时不一定会完全遵守, 1.JVM负责加载class文件并执行,因此,首先要掌握的是JDK如何将Java代码编译成clas ...

  5. Linux bash笔记

    关于bash脚本,一般开头都加#!/bin/bash,表示以bash来作为脚本解释器:如果不加的话,就会默认当前用户登陆的shell为脚本解释器(很多情况下为sh,sh与bash不同,有可能导致脚本无 ...

  6. mongoDB oplog的说明及应用

    mongoDB oplog 说明 ts:8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示.这个值很重要,在选举(如master宕机时)新primary时,会选择ts最大的那 ...

  7. Java学习——读写txt文件

    package HHH; import java.io.*; import static java.lang.System.out; public class OpenFile { public st ...

  8. nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模块的Javascript

    Javascrip本身不带IO功能,nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模 ...

  9. Oracle group by

    group by 简单点理解就是根据什么分组 为此 group by job   根据job进行分组 举例:在Oracle当中scott/oracle 下有emp表 进行如下操作  体会order b ...

  10. go语言学习--go的临时对象池--sync.Pool

    一个sync.Pool对象就是一组临时对象的集合.Pool是协程安全的. Pool用于存储那些被分配了但是没有被使用,而未来可能会使用的值,以减小垃圾回收的压力.一个比较好的例子是fmt包,fmt包总 ...