《算法》第五章部分程序 part 5
▶ 书中第五章部分程序,包括在加上自己补充的代码,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的更多相关文章
- 《算法》第五章部分程序 part 3
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(美国国旗排序) ● 美国国旗排序 package package01; import edu.princeton.cs.algs4 ...
- 《算法》第五章部分程序 part 8
▶ 书中第五章部分程序,包括在加上自己补充的代码,适用于基因序列的 2-Bit 压缩算法,行程长压缩算法,Huffman 压缩算法,LZW 压缩算法 ● 适用于基因序列的 2-Bit 压缩算法 pac ...
- 《算法》第五章部分程序 part 7
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串的二进制表示.十六进制表示.图形表示 ● 二进制表示 package package01; import edu.princeton.cs.al ...
- 《算法》第五章部分程序 part 6
▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(NFA),grep 命令(利用 NFA 匹配) ● 非确定性有穷自动机(NFA) package package01; impor ...
- 《算法》第五章部分程序 part 4
▶ 书中第五章部分程序,包括在加上自己补充的代码,Trie 树类,Trie 集合,三值搜索树(Ternary Search Trie) ● Trie 树类 package package01; imp ...
- 《算法》第五章部分程序 part 2
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(计数 + 插排),(原地排序),(三路快排,与前面的三路归并排序相同) ● 计数 + 插排 package package01; ...
- 《算法》第五章部分程序 part 1
▶ 书中第五章部分程序,包括在加上自己补充的代码,字母表类,字符串低位优先排序(桶排) ● 字母表类 package package01; import edu.princeton.cs.algs4. ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 《算法》第一章部分程序 part 1
▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...
随机推荐
- javascript 对象的原型
往往定义一个函数时,函数内部有关键字this时,就把这个函数当成对象,this相当于python中的self.都是对象中用到的,代表对象本身. js不像python,在函数内部定义的子函数,在每次创建 ...
- 数据挖掘标准规范之CRISP-DM基础
一.前言 每每提到数据挖掘,总有些人上来就是ETL.是算法.是数学模型,作为搞工程实施的我而言,很是头疼.其实作为数据挖掘的而言,算法只是其实现手段.是工具和实现手段而已,我们不是在创造算法(国外职业 ...
- gitlab 10.8.1 迁移
参考官网: https://docs.gitlab.com/ee/raketasks/backup_restore.html Backing up and restoring GitLab 及 ...
- etcd集群部署与遇到的坑(转)
原文 https://www.cnblogs.com/breg/p/5728237.html etcd集群部署与遇到的坑 在k8s集群中使用了etcd作为数据中心,在实际操作中遇到了一些坑.今天记录一 ...
- view之Scroller工具类和GestureDetector的简单用法
转载:http://ipjmc.iteye.com/blog/1615828 Android里Scroller类是为了实现View平滑滚动的一个Helper类.通常在自定义的View时使用,在View ...
- view之自定义控件
转载自:http://blog.163.com/ppy2790@126/blog/static/103242241201382210910473/ 开发自定义控件的步骤: 1.了解View的工作原理 ...
- Linux常用命令1-50(持续更新中)
1:echo $PATH (打印出PATH变量的值) 不同用户下面的PATH值有可能不一样 echo 有显示打印的意思 $ 表示后面的是一个变量的意思 PATH 变量 /usr ...
- 转]GSM模块信号强度CSQ与RSSI的对应关系
使用GSM或者3G模块时,都会接触到信号强度CSQ.通过指令AT+CSQ,模块返回当前的信号质量,例如: AT+CSQ +CSQ: 28,0 其中28就是信号强度CSQ,但它不是真实的CSQ,他应该叫 ...
- java eclipse maven The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path 解决方法
在eclipse 中使用maven 创建java web项目,启动服务器遇到提示:The superclass "javax.servlet.http.HttpServlet" w ...
- locals()和globals()
都是获取当前作用域的内容: locals() 获取局部作用域的所有内容 函数内:获取locals()之前的,当前作用阈所有内容 函数外:获取打印前, 当前的作用域所有内容 在闭包内: 会把使用到的外层 ...