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

● 非确定性有穷自动机(NFA)

 package package01;

 import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.DirectedDFS; public class class01
{ private Digraph graph; // 含 ε-转移 的有穷自动机图
private String regexp; // 输入的正则表达式
private final int m; // 正则表达式包含的字符数 public class01(String inputRegexp) // 根据正则表达式构造 NFA
{
regexp = inputRegexp;
m = regexp.length();
Stack<Integer> ops = new Stack<Integer>();
graph = new Digraph(m + 1); // 自动机的状态数比正则表达式多 1
for (int i = 0; i < m; i++)
{
int lp = i; // 指向当前操作数
if (regexp.charAt(i) == '(' || regexp.charAt(i) == '|') // 遇到 '(' 和 '|',压栈
ops.push(i);
else if (regexp.charAt(i) == ')') // 遇到 ')',吐栈
{
int or = ops.pop();
if (regexp.charAt(or ) == '|') // 吐出'|',需要添加两条边,设原文为 (A|B)
{
lp = ops.pop(); // 取出在此之前的 '('
graph.addEdge(lp, or +1); // 第一条边从 '(' 指向 '|' 的后一节点,表示支路 B
graph.addEdge(or , i); // 第二条边从 '|' 指向 ')' 之后,表示支路 A
}
else if (regexp.charAt(or ) == '(') // 吐出 '(',说明存在一个整体'(...)',用 lp 标记起点,服务于后边的 '*'
lp = or ;
else
assert false; // 栈顶是其他东西(遇不到该分支?因为没有把其他东西压入栈中)
}
if (i < m - 1 && regexp.charAt(i + 1) == '*') // 下一个字符是闭包,需要添加两条边
{
graph.addEdge(lp, i + 1); // 第一条边从当前节点指向 '*' 节点
graph.addEdge(i + 1, lp); // 第二条边从 '*' 节点指向当前节点,如果当前节点是 ')',则指向当前整体的起点处
}
if (regexp.charAt(i) == '(' || regexp.charAt(i) == '*' || regexp.charAt(i) == ')') // 如果当前符号是'(*)' 三者之一,则添加添加一条正常边指向下一节点
graph.addEdge(i, i + 1);
}
if (ops.size() != 0)
throw new IllegalArgumentException("Invalid regular expression");
} public boolean recognizes(String txt) // 使用生成的 NFA 识别输入的字符串
{
Bag<Integer> pc = new Bag<Integer>(); // 存放当前能够到达的所有节点
DirectedDFS dfs = new DirectedDFS(graph, 0); // 从节点 0 深度优先遍历,表示读取正文第 0 位之前就能通过 ε-转移 到达的所有状态,放入背包 pc 中
for (int v = 0; v < graph.V(); v++)
{
if (dfs.marked(v))
pc.add(v);
}
for (int i = 0; i < txt.length(); i++) // 循环每次取原文的一个字符
{
if (txt.charAt(i) == '*' || txt.charAt(i) == '|' || txt.charAt(i) == '(' || txt.charAt(i) == ')') // 被匹配的原文不能包含 '(*|)'
throw new IllegalArgumentException("text contains the metacharacter '" + txt.charAt(i) + "'");
Bag<Integer> match = new Bag<Integer>(); // 临时背包,用于存放能与 txt[i] 匹配的所有正则表达式的状态(“状态” 指的是节点编号)
for (int v : pc) // 遍历 pc,即以当前能够到达的所有状态为起点尝试匹配 txt[i]
{
if (v == m) // pc 包含节点 m,说明已经到达了终点,完成匹配
continue;
if ((regexp.charAt(v) == txt.charAt(i)) || regexp.charAt(v) == '.') // txt[i] 与正则表达式当前的某个状态可以匹配,向 match 中写入 v 的下一节点
match.add(v + 1);
}
dfs = new DirectedDFS(graph, match); // 以 match 所有元素为起点深度优先遍历,表示当前所有可达节点通过 ε-转移 到达的所有状态
pc = new Bag<Integer>(); // 更新 pc
for (int v = 0; v < graph.V(); v++)
{
if (dfs.marked(v))
pc.add(v);
}
if (pc.size() == 0) // pc 空,说明没有任何可达状态了,停止匹配
return false;
}
for (int v : pc) // 遍历 pc,如果包含节点 m,说明到达了终点,完成匹配
{
if (v == m)
return true;
}
return false;
} public static void main(String[] args)
{
String regexp = "(" + args[0] + ")", txt = args[1]; // 输入的正则表达式最外层用括号包住
class01 nfa = new class01(regexp);
StdOut.println(nfa.recognizes(txt));
}
}

● grep 命令实现

 package package01;

 import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.NFA; public class class01
{
private class01() {} public static void main(String[] args)
{
String regexp = "(.*" + args[0] + ".*)";
for (NFA nfa = new NFA(regexp); StdIn.hasNextLine();)
{
String line = StdIn.readLine();
if (nfa.recognizes(line))
StdOut.println(line);
}
}
}

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

  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 5

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

  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. Winfrom DataGridView中使用Tooltip

    第一步:添加DataGridView.Tooltip控件,略 第二步:设置ToolTip 相关属性,略,参考下图 第三步:DataGridView 添加 CellMouseEnter.CellMous ...

  2. TextBox限制输入字母、数字、退格键

    公共方法如下: /// <summary> /// 正则表达式验证只能输入数字或字母 /// </summary> /// <param name="pendi ...

  3. PCM简介

    1. 差分脉冲编码调制 如果两个相邻抽样值之间的相关性很大,那么它们的差值就较小,这样,仅对差值量化可以使用较少的比特数,此即差分PCM,或DPCM. 为了理论方便,我们将采样和量化分开,并用不带上三 ...

  4. DS二叉树--Huffman编码与解码

    题目描述 1.问题描述 给定n个字符及其对应的权值,构造Huffman树,并进行huffman编码和译(解)码. 构造Huffman树时,要求左子树根的权值小于.等于右子树根的权值. 进行Huffma ...

  5. Flashbuilder的bug FlashBuilder 1119: 访问可能未定义的属性 on (通过 static 类型

    FlashBuilder 1119: 访问可能未定义的属性 on (通过 static 类型 当此问题出现的时候 无论 刷新 清理 注释 删除 乃至重启电脑都无济于事. 解决方法:备份此类到另外一个地 ...

  6. centos7.4安装过程

    开启CPU虚拟化 按到install CentOS7 按下tab 输入net.ifnames=0 biosdevname=0 先来一段文档v1 ============================ ...

  7. centos7更改引导项等待时间

    centos7已经不用grub,改用grub2. [ root]# vi /boot/grub2/grub.cfg 找到并更改启动时间(timeout) [root]# grub2-mkconfig ...

  8. Servlet基础学习

    Servlet学习 Servlet是Server与Applet的缩写,是服务端小程序的意思.使用Java语言编写的服务器端程序,可以像生成动态的WEB页,Servlet主要运行在服务器端,并由服务器调 ...

  9. Java - 25 Java 接口

    Java 接口 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法. 接口并 ...

  10. Java - 14 Java 日期时间

    java.util包提供了Date类来封装当前的日期和时间. Date类提供两个构造函数来实例化Date对象. 第一个构造函数使用当前日期和时间来初始化对象. Date( ) 第二个构造函数接收一个参 ...