▶ 书中第五章部分程序,包括在加上自己补充的代码,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. Jenkins触发远程Job的几种方式

    本文叙述基于以下假设前提,将介绍三种在不同的jenkins服务器之间触发Job的方法: 本地Jenkins Server local.jenkins.com远程Jenkins Server remot ...

  2. html多个水平并列组件自适应等高的做法

    做一个div盒子,设置over-flow:hidden,设置高度为auto.然后再盒子里排列若干inline-block,inline元素. 自适应的话常用做法是用line-height,font-s ...

  3. linux找不到动态链接库 .so文件的解决方法

    linux找不到动态链接库 .so文件的解决方法 如果使用自己手动生成的动态链接库.so文件,但是这个.so文件,没有加入库文件搜索路劲中,程序运行时可能会出现找不到动态链接库的情形. 可以通过ldd ...

  4. [蓝桥杯]ALGO-15.算法训练_旅行家的预算

    问题描述 一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的).给定两个城市之间的距离D1.汽车油箱的容量C(以升为单位).每升汽油能行驶的距离D2.出发点每升汽油价格P和沿 ...

  5. [蓝桥杯]ALGO-86.算法训练_矩阵乘法

    问题描述 输入两个矩阵,分别是m*s,s*n大小.输出两个矩阵相乘的结果. 输入格式 第一行,空格隔开的三个正整数m,s,n(均不超过200). 接下来m行,每行s个空格隔开的整数,表示矩阵A(i,j ...

  6. ALGO-39_蓝桥杯_算法训练_数组排序去重

    问题描述 输入10个整数组成的序列,要求对其进行升序排序,并去掉重复元素. 输入格式 10个整数. 输出格式 多行输出,每行一个元素. 样例输入 样例输出 解题思路: 若输入的数字存在数组中,剔除,否 ...

  7. 解决搜狗高速模式及设置页面打不开的问题DisableFeature.reg

    搜狗浏览器安装问题1.安装的时候要选择自定义安装,去掉参加用户体验计划的√,否则可能安装不上.2.搜狗sogou_explorer_7.0_0111.exe,设置页面se://settings/?ca ...

  8. elasticsearch mapping demo

    curl -XPUT localhost:9200/local -d '{ "settings" : { "analysis" : { "analyz ...

  9. lucene 一次查询多个id

    在文本搜索中,有时也需要一次搜索多个id,这里id类似数据库里面的主键. 这个id在索引里面的倒排列表长度往往等于1. 例如:根据id=[1,2,4,6,7]查询索引 最最一般的思路是构造一个bool ...

  10. Elasticsearch 5.2.x 使用 Head 插件连接不上集群

    如果访问elasticsearch出现跨域的问题,如下: 修改elasticsearch.yml文件 vim $ES_HOME$/config/elasticsearch.yml # 增加如下字段 h ...